This four-part blog post series covers the challenges developers face trying to incorporate modern Angular apps in SharePoint Framework projects, two approaches to incorporating Angular projects in SPFx project, and finishes off with the ‘good, bad & the ugly’ review.
You can get all four parts of the series here: Using Angular Elements in SharePoint Framework projects
Check out our Mastering the SharePoint Framework course that contains a chapter on using Angular Elements in SharePoint Framework projects including step-by-step demonstrations on how to make this work using the latest version of Angular.
This has been a long time coming and many of you Angular developers who also work with the SharePoint Framework may have given up by now and switched to React, but for those of you who have been holding out, Angular is finally a viable option for SPFx development even with a few drawbacks.
While you’ve been able to build full blown Angular apps using Angular v5 (released November 2017) or Angular v6 (released May 2018), it hasn’t been a good option. The primary reason is because you need a single root component that knows about everything on the page, something I wrote about in my post What’s up with Angular & the SharePoint Framework. This is an issue because there’s significant complexity when you have more than one SPFx Angular-based web part on the page. Considering it’s quite common to have more than one web part on a page and that we rarely know what’s going to be put on a SharePoint page when we are building our web parts, well, you see why this can be a problem.
Enter Angular Elements
So what’s the answer to this? Angular Elements! these allow you to build custom elements using Angular that can be used in any web project. This includes other Angular projects, React projects, VueJS projects, those that use jQuery or no framework at all and even SharePoint Framework projects. You can learn more about them in my post Solve the SharePoint Framework + Angular Challenge with Angular 5.0 Elements.
Too much to read? The TL;DR on that post is that you create a custom element (aka: web component) using Angular Elements which is encapsulated in a single JavaScript file. You add that file to the page which self-bootstraps the web component to the page giving you a new custom HTML element like this:
<microsoft-cloud-show episode="200" onPlay="doSomething()"></microsoft-cloud-show>
You don’t need anything else on the page to make it work. Angular Elements bootstrap themselves to the DOM when the page loads and include the all the necessary bits, including the parts of Angular it uses, in the single JavaScript bundle.
Creating Custom Elements with Angular Elements
For this example, let’s assume we have a very simple Angular component that will serve as our custom element. This custom element contains one input and one output:
import {
Component,
OnInit,
Input,
Output,
EventEmitter
} from '@angular/core';
@Component({
selector: 'app-hello-world',
template: `
<div>
<div><strong>Message:</strong> {{ message }}</div>
<input #sendBackMessage
type="text"
placeholder="message to send back to the web part" />
<button (click)="onButtonClicked(sendBackMessage.value)">
send message to hosting web part
</button>
</div>
`
})
export class AppComponent {
@Input()
public message: string;
@Output()
public elementButtonClick = new EventEmitter<string>();
public onButtonClicked(sendBackMessage: string) {
this.elementButtonClick.emit(sendBackMessage);
}
}
Unlike normal Angular applications, you need to change the way the application is bootstrapped. This is where Angular Elements comes into play. First, add the @angular/elements package into your project:
# using the Angular CLI
ng add @angular/elements
# using a package manager
yarn add @angular/elements
Now, update the root module. Assuming your custom element is called AppComponent, your root app module will look like this:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
entryComponents: [ AppComponent ]
})
export class AppModule {
constructor(private injector: Injector) {}
public ngDoBootstrap() {
if (!customElements.get('app-hello-world')) {
const AppElement = createCustomElement(AppComponent, { injector: this.injector });
customElements.define('app-hello-world', AppElement);
}
}
}
Notice the addition of the ngDoBootstrap()
method. This will imperatively bootstrap the custom element when the script file loads in the browser. Normally this is done by the @NgModule.bootstrap
property, but as you can see we removed it in the case of Angular Elements.
Using Custom Elements in SharePoint Framework Projects
Once you’ve created your custom element with Angular Elements, using it in a SPFx project is simple. After importing it into your web part, using it is as simple as this:
export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> {
public render(): void {
this.domElement.innerHTML =
`<app-hello-world message="${ this.properties.description }"></app-hello-world>`;
// attach to the custom elements 'elementButtonClick` event
this.domElement.getElementsByTagName('app-hello-world')[0]
.addEventListener('elementButtonClick', (event: any) => {
console.log(`The custom element sent the following message: ${ event.detail }`);
});
}
}
And with that, it just works!
The challenge is in creating the custom element… the challenge is in how you will organize your projects on your dev environment. You have two options on how you are going to implement this. I’ll discuss each of these two options in my next two posts…
A Word on Custom Elements and Browser Support
At the start of this post, I mentioned that custom elements are somewhat new. They are one aspect of something called web components. Web components are really made up of four different specifications. Another specification is the Shadow DOM, something else you may be familiar with.
Most of the browsers have accepted custom elements and the shadow DOM as a standard and have either implemented them in their stable builds, or they are currently under development. You can see the full matrix at the Web Components site, but here’s the TL;DR:
- Custom elements: Implemented in Chrome, Opera & Safari; Firefox is currently implementing & there’s a polyfill to address it until then. Edge, on the other hand, is still considering it, but it is supported with a polyfill.
- Shadow DOM: Implemented in Chrome; Opera, Safari, and FireFox are currently implementing & there’s a polyfill to address it until then. Yet again, Edge is still just considering it, but polyfills fix their shortcoming.
Edge disappoints yet again….
Anyway… in order for this to work in all experiences, we’re going to have to implement a few polyfills. Thankfully each one will do a feature check and only implement it if needed.
This four-part blog post series covers the challenges developers face trying to incorporate modern Angular apps in SharePoint Framework projects, two approaches to incorporating Angular projects in SPFx project, and finishes off with the ‘good, bad & the ugly’ review.
You can get all four parts of the series here: Using Angular Elements in SharePoint Framework projects
Check out our Mastering the SharePoint Framework course that contains a chapter on using Angular Elements in SharePoint Framework projects including step-by-step demonstrations on how to make this work using the latest version of Angular.