A set of simple reusable Angular controls to use within SharePoint for rapid prototyping and development. Originally in AngularJS/JavaScript and progressed to run within Angular (2+) within an included SPFx wrapper. This gives table controls, dynamic form fields driven off list data and type from schema and some helper utils.
This repository contains three main related pieces:
sharepoint-choice: the Angular librarysample: a standalone Angular sample app that consumes the libraryangular-wrapper: an SPFx web part that loads a bundled Angular app from a document library folder and renders it on the page
Additionally this has
sharepoint-choice.json: a sample file to upload to the tenancy app catalog under /sites/AppCatalog/SiteAssets which houses reused config settings between all apps on the tenancyAngularJS: the start of this journey and kept only for nostalgia
The library is aimed at SharePoint-hosted Angular experiences, including:
- dynamic field rendering from SharePoint list metadata
- editable, cached data tables for large datasets
- PnP-powered helpers for list data, files, folders, search, and MSAL-backed API calls
- attachment handling with extraction, metadata, and archive workflows
Exports:
SharepointChoiceComponentSharepointChoiceTableSharepointChoiceUtilsSharepointChoiceLoggingSharepointChoiceRefresh- shared models and interfaces
Public API entry point:
sharepoint-choice/src/public-api.ts
The sample app demonstrates how to:
- bootstrap a standalone Angular component into a page
- render
SharepointChoiceComponent - render
SharepointChoiceTable - use
SharepointChoiceUtilsagainst a SharePoint site
The sample component selector is:
<app-sample></app-sample>This is an SPFx web part named Angular Wrapper. It loads client-side assets from a folder and injects a chosen selector into the page. Its key web part properties are:
Folder: URL to the script folderSelector: Angular selector tag nameAdditional: additional attributes to place on the elementESBuild used: whether the target bundle is emitted in the newer Angular build format
- Node.js compatible with the package being built
- npm
- Angular CLI 22 for the Angular library and sample app
Install Angular CLI locally where needed:
npm install @angular/cli@22From sharepoint-choice:
npm install
npm run buildThis packages the library to:
sharepoint-choice/dist/sharepoint-choice
From sample:
npm install
npm run spc
npm run bundleNotes:
npm run spcbuilds the localsharepoint-choicepackage first- the sample app uses Angular's
@angular/build:applicationbuilder - with that builder, browser assets are emitted under the
browseroutput directory by default
Given the current sample/angular.json, the bundled browser assets are expected under:
sample/dist/sample/browser
If you want to test against SharePoint pages over localhost, generate a dev certificate:
npm install -g office-addin-dev-certs
office-addin-dev-certs install --days 3650Then place the generated certificate files where the sample app expects them:
sample/localhost.crtsample/localhost.key
If you want to host the built Angular app in SharePoint and execute it on a page through SPFx, this repo already contains the wrapper pattern.
For ESBuild used = true, the wrapper loads these files from the configured folder:
styles.csspolyfills.jsmain.js
For ESBuild used = false, it instead expects:
polyfills.jsruntime.jsmain.jsstyles.css
The sample app uses Angular's application builder, so the correct wrapper setting for the sample is:
ESBuild used = true
- Build the library in
sharepoint-choice. - Build the sample app in
sample. - Upload the emitted browser assets to a SharePoint document library folder.
- Add the
Angular Wrapperweb part to a page. - Point the web part at the uploaded folder.
- Set the selector to the Angular root component you want to bootstrap.
Because the sample build uses the default Angular browser output subfolder, you have two valid deployment options.
Example upload target:
SiteAssets/sample/v1
In that case, set the wrapper property values to:
Folder:SiteAssets/sample/v1Selector:app-sampleESBuild used: checked
Example upload target:
SiteAssets/sample/v1/browser
In that case, set the wrapper property values to:
Folder:SiteAssets/sample/v1/browserSelector:app-sampleESBuild used: checked
If you upload the bundle into the tenant app catalog site's SiteAssets library, the same pattern applies. Use the folder that directly contains main.js, polyfills.js, and styles.css.
Examples:
- site-relative from the app catalog site:
SiteAssets/sample/v1/browser - server-relative:
/sites/AppCatalog/SiteAssets/sample/v1/browser - absolute:
https://tenant.sharepoint.com/sites/AppCatalog/SiteAssets/sample/v1/browser
The wrapper accepts all three forms and normalizes them before loading the assets.
For the sample app:
Selector:app-sample
You can also pass extra attributes through the wrapper's Additional property. The wrapper already injects a context attribute containing the current SharePoint web URL.
SharepointChoiceComponent is a standalone Angular component that renders SharePoint field UI from field metadata and a backing form object.
The component currently contains renderers for:
BooleanMultiChoiceChoiceIntegerNumberCurrencyDateTimeTextLookupNoteGeolocationURLAttachmentsUserUserMulti
Notes:
Lookupincludes built-in SharePoint list search againstLookupListandLookupField, and keeps a display label visible for loaded or disabled valuesCurrencyuses locale-aware display and parsing based on the field LCID, with shared locale and currency-region maps exported fromsharepoint-choice.models.tsGeolocationrenders latitude and longitude inputs and shows a map link when both coordinates are present- the shared field model still includes
LookupMulti, but this component does not currently contain a dedicated render case for it - rich text note fields are supported through
@bobbyquantum/ngx-editorwhen the field metadata indicates rich text
Currency formatting is not hardcoded to a single symbol. The component maps SharePoint LCIDs to BCP 47 locales and then maps locale regions to currency codes.
The current built-in coverage includes a broad set of common SharePoint locales and countries, including examples such as:
- United States and United Kingdom
- most common Euro-area locales such as Germany, France, Spain, Italy, Portugal, Belgium, Slovakia, Slovenia, Estonia, Latvia, and Lithuania
- Switzerland, Norway, Sweden, Denmark, Poland, Czech Republic, Hungary, Romania, Croatia, Serbia, Russia, and Ukraine
- Saudi Arabia, Israel, Turkey, Pakistan, India, Thailand, Malaysia, Indonesia, Kazakhstan, Vietnam, China, Taiwan, Hong Kong, Singapore, and Macau
- Brazil, Japan, and South Korea
If an LCID or region is not in the built-in map, the component falls back to en-US and USD.
| Input | Type | Description |
|---|---|---|
form |
SharepointChoiceForm |
Backing object for the field values. |
spec |
`SharepointChoiceList | undefined` |
override |
`string | SharepointChoiceField |
disabled |
boolean |
Disables the control. |
versions |
`SharepointChoiceForm[] | undefined` |
prefix |
string |
Prefix added to generated form control names. |
field |
string |
Internal field name to bind. |
text |
object |
Text and note field configuration. |
select |
object |
Choice field configuration. |
file |
object |
Attachment and document field configuration. |
tooltip is internal component state, not a public input.
{
pattern?: string;
height?: number;
width?: number;
search?: Function;
select?: Function;
parent?: any;
}| Property | Description |
|---|---|
pattern |
Regex validation pattern for text input. |
height |
Textarea height in pixels. |
width |
Minimum width in pixels. |
search |
Async callback for autocomplete results. |
select |
Callback when an autocomplete item is chosen. |
parent |
Optional callback context object. |
{
none?: string;
other?: string;
filter?: Function;
}| Property | Description |
|---|---|
none |
Label for a null option. |
other |
Label for an "Other" option. |
filter |
Function used to filter available choices. |
{
extract?: boolean;
check?: boolean;
accept?: string;
download?: boolean;
uploadonly?: boolean;
archive?: string;
view?: number;
doctypes?: string[];
doctype?: string;
notes?: string;
spec?: SharepointChoiceList;
}| Property | Description |
|---|---|
extract |
Extract ZIP, EML, or MSG contents where supported. |
check |
Show checkboxes against files. |
accept |
File input accept filter. |
download |
Force download links instead of opening a new tab. |
uploadonly |
Hide existing files and only allow upload interactions. |
archive |
Field name used to mark archived files. |
view |
0 all, 1 not archived, -1 archived. |
doctypes |
Allowed document type values. |
doctype |
Field name that stores document type. |
notes |
Field name used for file notes. |
spec |
Additional field metadata used for file-level editing. |
@Output() change = new EventEmitter<{
field: string;
value: any;
target: HTMLElement;
}>();| Property | Description |
|---|---|
field |
Internal field name. |
value |
Updated value. For multi-value structures, .results is emitted where applicable. |
target |
The component host element, not necessarily the inner native control that triggered the update. |
- initializes SharePoint-shaped values for fields such as
MultiChoice,UserMulti,URL, andAttachments - supports autocomplete on text and people fields
- supports drag and drop from local files, Teams library links, and Outlook add-in flows
- supports file extraction for ZIP, EML, and MSG uploads
- emits refresh notifications through
SharepointChoiceRefreshso sibling instances using the same backing references can update underOnPushchange detection
SharepointChoiceTable is a standalone Angular table component for SharePoint-style datasets.
- multiple tabs over one table surface
- persisted sort, filter, hidden-column, tab, and page-size state via local storage
- paging
- row selection with emitted selected rows
- inline editing using
SharepointChoiceComponent - column hide/show, resize, and reorder
- hyperlink rows
- custom cell renderers
- Excel export
- memoized row, column, page, and renderer caching
| Input | Type | Description |
|---|---|---|
allData |
SharepointChoiceTabs |
Full dataset keyed by tab name. |
allCols |
SharepointChoiceColumn[] |
Column definitions. |
allTabs |
string[] |
Optional explicit tab order. |
selectedTab |
`string | undefined` |
pageSize |
number |
Rows per page. |
loading |
boolean |
Loading state. |
search |
`SharepointChoiceRowChild | undefined` |
prefix |
string |
Prefix for local storage keys. |
tableHeight |
string |
Table container height. |
allEditing |
boolean |
Render editable cells in edit mode by default. |
allowHideColumns |
boolean |
Enable hide/show UX. |
showEmptyTabs |
boolean |
Include tabs with no rows. |
allowSelection |
boolean |
Enable row selection UI. |
rowClicked |
`Function | undefined` |
hyperlinkRow |
`Function | undefined` |
hyperlinkTarget |
string |
Link target for hyperlink rows. |
export |
`SharepointChoiceExportOptions | undefined` |
| Output | Type | Description |
|---|---|---|
selected |
EventEmitter<{ data, tab }> |
Emits selected rows for the active tab. |
cleared |
EventEmitter<void> |
Emits when table state is cleared. |
clicked |
EventEmitter<{ row, target }> |
Emits row click data when no callback overrides it. |
| Cache | Purpose |
|---|---|
_colsCache |
Computed visible columns per tab |
_rowsCache |
Filtered and sorted rows per tab |
_pageCache |
Current page slice |
_fieldMapCache |
Parsed dot-path field segments |
_nodeCache |
Cached renderer node output |
SharepointChoiceUtils wraps PnP JS, SharePoint REST behavior, and MSAL-backed API access.
constructor(context?: string)If no context is supplied, the utility attempts to derive one from the current SharePoint page.
| Property | Type | Description |
|---|---|---|
context |
string |
Resolved site URL used for SharePoint operations. |
sp |
SPFI |
PnP JS instance configured against context. |
| Method | Returns | Purpose |
|---|---|---|
permissions() |
Promise<SharepointChoicePermission> |
Builds a flattened permission or group map for the current user. |
hasPermission(object, permissions) |
Promise<boolean> |
Checks effective SharePoint permissions against PermissionKind values. |
search(query, limit?, page?, sort?, select?, detail?, filter?) |
Promise<SearchResults> |
Runs SharePoint search. |
fields(listTitle) |
Promise<SharepointChoiceList> |
Loads and normalizes list field metadata. |
data(id, listTitle) |
Promise<SharepointChoiceForm> |
Loads one list item and normalizes values. |
version(id, listTitle, spec?) |
Promise<SharepointChoiceForm[]> |
Loads version history and computes changed fields. |
msalApi(endpoint, tokenRole, method?, body?, dataType?, environment?) |
Promise<any> |
Calls a mapped API using MSAL. |
callApi(tenant, clientId, scope, url?, method?, body?, dataType?) |
Promise<any> |
Generic MSAL-authenticated API caller. |
save(form, original, listTitle) |
Promise<number> |
Creates or updates a SharePoint list item and attachments. |
param(name) |
`string | undefined` |
ensurePath(path, start) |
Promise<void> |
Ensures a folder path exists. |
getRoot(list) |
Promise<string> |
Returns a list root folder URL. |
getFiles(path, additional?) |
Promise<SharepointChoiceAttachment[]> |
Loads files and list item metadata from a folder. |
relocateFolder(source, destination) |
`Promise<string | null>` |
saveFiles(path, additional?, url?, files?, metadata?) |
Promise<void> |
Saves uploaded files and applies metadata. |
permissions() does not compute full effective SharePoint permissions in the same way as hasPermission(). It builds a flat perms object from:
- current SharePoint group memberships
- optional entries from a hidden
Securitylist when present, this can be used with a list item per permission and secured to only that permission to facilitate nested permission discovery which can happen in some tenancies.
Use hasPermission() when you need an object-level effective-permission check against SharePoint permission kinds.
MIT