Building SharePoint Framework Client-Side Web Parts with Angular
You can use Angular to build Client-Side Web Parts on the new SharePoint Framework. Here is how to get started and what to keep in mind.
Building SharePoint Framework Client-Side Web Parts with Angular
Angular is one of the most popular and widely adopted frameworks for building client-side solutions. Thanks to its modularity it can be used for anything ranging from complex multi-view Single Page Applications to smaller components such as Web Parts. Many organizations have been using Angular for building SharePoint solutions in the past and now, that the developer preview of the SharePoint Framework is released, you might wonder if you can use Angular for building Client-Side Web Parts.
The SharePoint Framework Yeoman generator allows you to create Client-Side Web Parts using React, Knockout or using no particular JavaScript framework. Despite the fact that it doesn’t list Angular as one of the options, you can perfectly use Angular v1.x for building Client-Side Web Parts on the SharePoint Framework.
The thing about using Angular 2 for building Client-Side Web Parts
Comparing to Angular v1.x, Angular 2 uses a different mechanism for bootstrapping applications. While using Angular 2 for building Client-Side Web Parts seems to be working at first, if you would add multiple instances of the same Web Part to the page, only the first one would be bootstrapped.
The good news is that the engineering team responsible for the SharePoint Framework is aware of this issue and is looking into it. If there is a proper way of using Angular 2 with the SharePoint Framework, we will see it as one of the options in the future. But for now you can use Angular v1.x.
Building Angular Client-Side Web Parts with style
A while back fellow-MVP Andrew Connell started the ngOfficeUIFabric initiative - a set of Angular directives to help developers use the Office UI Fabric for styling their SharePoint and Office solutions built with Angular. By using Office UI Fabric, third party solutions feel like a part of Office/SharePoint. ngOfficeUIFabric not only significantly simplifies using Office UI Fabric in Angular but also offers some added functionality such as sortable tables.
When building Client-Side Web Parts on the SharePoint Framework with Angular v1.x I would highly recommend you use ngOfficeUIFabric.
Getting started building SharePoint Framework Client-Side Web Parts with Angular
To illustrate how to use Angular v1.x with ngOfficeUIFabric I’ve built a sample Web Part that communicates with SharePoint using its REST API to show a list of to do items.
The source code is available on GitHub at https://github.com/waldekmastykarz/spfx-angular-ts-todo.
Using Angular in Client-Side Web Parts
At this moment Angular isn’t amongst the framework listed in the SharePoint Framework Yeoman generator. If you want to use Angular in your Client-Side Web Part, you should start with creating a Web Part using no particular JavaScript framework.
In the developer preview of the SharePoint Framework loading Angular and ngOfficeUIFabric from CDN doesn’t seem to be working and the only way to use them is by including them in the Web Part bundle.
Let’s start by installing Angular and ngOfficeUIFabric:
$ npm i angular ng-office-ui-fabric -S
The -S parameter will add both packages to the list of dependencies in the package.json file. When other developers get the project from source control and restore its dependencies using the npm i command, it will automatically include both Angular and ngOfficeUIFabric.
Next, let’s install Angular typings for TypeScript:
$ tsd install angular --save
Finally, in the Web Part’s main code file, let’s reference Angular (we will reference ngOfficeUIFabric from our Angular application later on):
import * as angular from 'angular';
Creating the Angular application
There is nothing specific to SharePoint Framework in creating an Angular application. If you’ve worked with Angular in the past, you will be able to leverage everything you know. And since SharePoint Framework uses TypeScript you don’t need to configure anything yourself and can directly start with writing your application. While using TypeScript for a Web Part might seem excessive, you will appreciate its type safety features particularly when refactoring your code or picking up code written by another developer.
Here are excerpts from the Angular application files used by the sample To do Web Part. Their full source code is available on GitHub.
import * as angular from 'angular';
import HomeController from './HomeController';
import DataService from './DataService';
import 'ng-office-ui-fabric';
const todoapp: ng.IModule = angular.module('todoapp', [
'officeuifabric.core',
'officeuifabric.components'
]);
todoapp
.controller('HomeController', HomeController)
.service('DataService', DataService);
This is the main file that instantiates the application module. We’re loading the controller, the service and ngOfficeUIFabric which we then hook up to the application module.
import { IDataService, ITodo } from './DataService';
export default class HomeController {
}
The HomeController is the main controller for our Web Part that controls the view model and communicates with the DataService for data manipulation. It’s also responsible for reacting to Web Part property changes and passing them to the rest of the application.
export interface ITodo {
}
interface ITodoItem {
}
export interface IDataService {
}
export default class DataService implements IDataService {
}
The DataService is responsible for communicating with SharePoint for data retrieval and manipulation. It defines a number of interfaces: IDataService representing the service, ITodo representing a to do item in the application and ITodoItem representing a to do item in the SharePoint List.
With the Angular files in place the next step is to hook them up in the Client-Side Web Part.
Loading the Angular application in a Client-Side Web Part
Our sample application consists of a single view with no additional routes. We can define the template directly in the Web Part’s DOM element and use it to bootstrap the application:
public render(): void {
if (this.renderedOnce === false) {
this.domElement.innerHTML = `
<div class="${styles.toDoWebPart}">
<div data-ng-controller="HomeController as vm">
<div class="${styles.configurationNeeded}" ng-show="vm.configurationNeeded">
Please configure the Web Part
</div>
<div ng-show="vm.configurationNeeded === false">
<div class="${styles.loading}" ng-show="vm.isLoading">
<uif-spinner>Loading...</uif-spinner>
</div>
<div id="entryform" ng-show="vm.isLoading === false">
<uif-textfield uif-label="New to do:" uif-underlined ng-model="vm.newItem" ng-keydown="vm.todoKeyDown($event)"></uif-textfield>
</div>
<uif-list id="items" ng-show="vm.isLoading === false" >
<uif-list-item ng-repeat="todo in vm.todoCollection" uif-item="todo" ng-class="{'${styles.done}': todo.done}">
<uif-list-item-primary-text>{{todo.title}}</uif-list-item-primary-text>
<uif-list-item-actions>
<uif-list-item-action ng-click="vm.completeTodo(todo)" ng-show="todo.done === false">
<uif-icon uif-type="check"></uif-icon>
</uif-list-item-action>
<uif-list-item-action ng-click="vm.undoTodo(todo)" ng-show="todo.done">
<uif-icon uif-type="reactivate"></uif-icon>
</uif-list-item-action>
<uif-list-item-action ng-click="vm.deleteTodo(todo)">
<uif-icon uif-type="trash"></uif-icon>
</uif-list-item-action>
</uif-list-item-actions>
</uif-list-item>
</uif-list>
</div>
</div>
</div>`;
angular.bootstrap(this.domElement, ['todoapp']);
}
}
Notice how the template is wrapped in the if (this.renderedOnce === false)
clause. The render
function of Client-Side Web Parts is called initially, when the Web Part is added to the page, but also every time one of its properties is changed in the Property Pane. Because an Angular application should be bootstrapped only once, we use the renderedOnce
property to verify that the Web Part is rendering initially and bootstrap it.
Also notice how the template uses ngOfficeUIFabric directives (HTML tags starting with uif-). Without them, the template using Office UI Fabric would be bigger and more complex.
At this point, the Web Part should be working and you should be able to see and interact with to do items from your SharePoint List.
Using a newer version of the Office UI Fabric
Although the SharePoint Framework ships with Office UI Fabric there are situations when you might want to use a newer version than the one available out of the box.
Until recently the Office UI Fabric has been built independently of the SharePoint Framework. Looking closely at our Web Part you might notice, that while it’s styled using the Office UI Fabric, it doesn’t quite look as it’s supposed to: see the bullet next to each to do item?
This isn’t a bug in ngOfficeUIFabric. Looking at the source code you can see that SharePoint Workbench uses Office UI Fabric v2.0.1, while ngOfficeUIFabric is using Office UI Fabric v2.6.1. Luckily using a newer version of Office UI Fabric with the SharePoint Framework isn’t a problem. You can choose whether you would prefer to include Office UI Fabric in your Web Part’s bundle or to load it from CDN. The latter option makes more sense when there are multiple Web Parts using Office UI Fabric on the portal.
In order to load Office UI Fabric CSS from a CDN we first need to load the SPModuleLoader class, by adding the following import statement to the Web Part’s code:
import ModuleLoader from '@microsoft/sp-module-loader';
In Drop 6 of the SharePoint Framework the ModuleLoader class has been renamed to SPModuleLoader. Also the sp-module-loader package has to be excluded from the bundle by adding to the config/config.json file externals section:
"@microsoft/sp-module-loader": "node_modules/@microsoft/sp-module-loader/dist/sp-module-loader.js"
Next, in the Web Part’s constructor we use the loadCss
function to load Office UI Fabric CSS:
public constructor(context: IWebPartContext) {
super(context);
ModuleLoader.loadCss('https://appsforoffice.microsoft.com/fabric/2.6.1/fabric.min.css');
ModuleLoader.loadCss('https://appsforoffice.microsoft.com/fabric/2.6.1/fabric.components.min.css');
}
In Drop 6 of the SharePoint Framework you should use SPModuleLoader instead of ModuleLoader.
After reloading SharePoint Workbench we can now see that the newer version of Office UI Fabric is being used and our to do items are being rendered as expected.
Passing Web Part’s configuration into Angular
Web Parts are truly powerful when they can be configured to match the specific scenario. SharePoint Framework makes it extremely easy to specify configurable Web Part properties and it supports different types of values and controls to configure them.
Once the configuration is completed, it should be passed into Angular, which might not seem a big issue at first, but becomes more complex when you think of it. Web Part properties are configured in the Web Part outside of Angular and there could be multiple instances of your Web Part on the page using different configuration options. Using the global scope might not be the best place to store your settings and even then how would you pass them into your Angular application?
One way of doing it, is by broadcasting an event on the root scope every time a Web Part property is changed. This can be done from the Web Part. By subscribing to the event, the Angular application can respond to the changes and handle them accordingly. Here is how it’s done.
When bootstrapping the Angular application we need to get a reference to $injector
:
export default class ToDoWebPartWebPart extends BaseClientSideWebPart<IToDoWebPartWebPartProps> {
private $injector: ng.auto.IInjectorService;
public render(): void {
if (this.renderedOnce === false) {
// omitted for brevity
this.$injector = angular.bootstrap(this.domElement, ['todoapp']);
}
}
}
We need to store that reference in a separate class variable because the value will be assigned only once, during the initial render, but we will need it every time Web Part’s properties are changed.
Next, using the $injector
we broadcast an event every time the render
function is triggered (meaning one of the Web Part properties has changed):
export default class ToDoWebPartWebPart extends BaseClientSideWebPart<IToDoWebPartWebPartProps> {
private $injector: ng.auto.IInjectorService;
public render(): void {
if (this.renderedOnce === false) {
// omitted for brevity
this.$injector = angular.bootstrap(this.domElement, ['todoapp']);
}
this.$injector.get('$rootScope').$broadcast('configurationChanged', {
sharePointApi: this.properties.sharePointApi,
todoListName: this.properties.todoListName,
hideFinishedTasks: this.properties.hideFinishedTasks
});
}
}
Next, in the controller of our Angular application we subscribe to that event and process the new settings:
export default class HomeController {
constructor(private dataService: IDataService, private $window: ng.IWindowService, private $rootScope: ng.IRootScopeService) {
const vm: HomeController = this;
this.init(null, null);
$rootScope.$on('configurationChanged', (event: ng.IAngularEvent, args: { sharePointApi: string; todoListName: string; hideFinishedTasks: boolean }): void => {
vm.init(args.sharePointApi, args.todoListName, args.hideFinishedTasks);
});
}
}
With that setup, every time a Web Part property is changed, it will broadcast an Angular event that the HomeController will pick up and process refreshing the application and reloading data if necessary.
Summary
SharePoint Framework is a flexible framework for building SharePoint customizations. By leveraging your existing Angular skills you can easily build powerful Client-Side Web Parts with very little SharePoint Framework-specific code.
Check out the sample Angular SharePoint Framework Client-Side Web Part on GitHub at https://github.com/waldekmastykarz/spfx-angular-ts-todo.