SPFx Webpart With Angular Elements

Introduction

 
As developers, we know SPFx and Angular both are the strongest frameworks. Recently PnP generator spfx released version 1.14.0 with Angular 9 support. So, in this article, we will see step by step implementation of SPFx webpart integration with Angular elements.
 

What are Angular Elements? 

 
Angular Elements is one of the best features released in Angular 6. It allows us to create Web Components (Custom Elements) using Angular. Angular elements are ordinary Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way. This approach lets us develop reusable components in a way that’s familiar to us, and yet, embed these in every kind of website (vanilla JavaScript, React, Vue, WordPress, etc. ).
 
Prerequisites And Environment Setup
 
To develop SPFx webpart with Angular Elements, we need  Angular CLI and PnP Generator SPFx. 
  • Angular CLI is a command-line interface for Angular. This gives various commands to scaffold projects, generate components, services, modules, and many more things.
  • PnP/generator-SPFx  provides improved governance for SharePoint Framework projects. it extends the capabilities for ReactJS, and Knockout projects and support for additional frameworks, such as HandlebarsJS, Aurelia, VueJS and Angular Elements. 
Now we will install both packages globally using the below command,
  1. npm install -g @angular/cli @pnp/generator-spfx
Let's start the development step by step.
 

Development 

  • Open a command prompt 
  • Move to the path where you want to create a project
  • Create a project directory using 
  1. md directory-name
Move to the above-created folder using
  1. cd directory-name
Now execute the below command to create a solution
  1. yo @pnp/spfx
It will ask some questions as below,
 
Project Setup
 
These commands will generate separate folders for angular elements and SPFx webpart with -spfx suffix.
 
After successful installation, an open project in VS Code executes the below command
  1. code .    
Folder Structure 
 
Demo Application 
 
In this demo, we will get SharePoint list records and display it in webpart so let's the start step by step implementation.
 
Install bootstrap (for some designing purpose) in the Angular project executing the below command, 
  1. npm install bootstrap
Set the bootstrap path in angular.json file as below,
  1. ...  
  2. "architect":{  
  3. "build":{  
  4. ...  
  5.    "styles": [  
  6.       "node_modules/bootstrap/dist/css/bootstrap.min.css",  
  7.       "src/styles.scss"   
  8.    ]  
  9. ...     
  • Now we will create a service (to perform backend API calls) and model in Angular project. For more details refer to this article.
  • Our Angular project structure now looks like this after creating models and services:
Angular Project Structure
Now we will update the required files. 
 
todo.model.ts
  1. export interface ToDo {  
  2.     Id?: number;  
  3.     Title: string;  
  4. }   
app.module.ts
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule, Injector } from '@angular/core';  
  3. import { createCustomElement } from '@angular/elements';  
  4.   
  5. import { SpFxAngular9WebPartComponent } from './sp-fx-angular9-web-part/sp-fx-angular9-web-part.component';  
  6. import { HttpClientModule } from '@angular/common/http';  
  7.   
  8. @NgModule({  
  9.   declarations: [  
  10.     SpFxAngular9WebPartComponent  
  11.   ],  
  12.   imports: [  
  13.     BrowserModule,  
  14.     HttpClientModule  
  15.   ],  
  16.   providers: [],  
  17.   entryComponents: [SpFxAngular9WebPartComponent]  
  18. })  
  19. export class AppModule {  
  20.   constructor(private injector: Injector) {}  
  21.   
  22.   ngDoBootstrap() {  
  23.     const el = createCustomElement(SpFxAngular9WebPartComponent, { injector: this.injector });  
  24.     customElements.define('app-sp-fx-angular9-web-part', el);  
  25.   }  
  26. }  
app.service.ts
  1. import { Injectable } from '@angular/core';  
  2. import { HttpClient, HttpHeaders } from '@angular/common/http';  
  3. import { ListItems } from '../models/listItems.model';  
  4. import { map, tap, catchError } from 'rxjs/operators';  
  5. @Injectable({  
  6.   providedIn: 'root'  
  7. })  
  8. export class AppService {  
  9.   private BASE_URL = 'https://vtsp.sharepoint.com/';  
  10.   constructor(private httpClient: HttpClient) {  
  11.   }  
  12.   setAPIUrl(url: string) {  
  13.     this.BASE_URL = url || this.BASE_URL;  
  14.   }  
  15.   
  16.   getListItems() {  
  17.     return this.httpClient.get<any>(`${this.BASE_URL}/_api/web/lists/getbytitle('React')/items?$select=Id,Title`).pipe(  
  18.       map(response => response.value as ListItems[])  
  19.     ).toPromise();  
  20.   }  
  21. }   
[WebpartName].component.ts
  1. import { Component, Input, OnInit, ViewEncapsulation, Output, EventEmitter, OnDestroy } from '@angular/core';  
  2. import { ListItems } from '../models/listItems.model';  
  3. import { AppService } from '../services/app.service';  
  4. @Component({  
  5.   selector: 'app-sp-fx-angular9-web-part',  
  6.   templateUrl: './sp-fx-angular9-web-part.component.html',  
  7.   styleUrls: ['./sp-fx-angular9-web-part.component.scss'],  
  8.   encapsulation: ViewEncapsulation.Emulated  
  9. })  
  10. export class SpFxAngular9WebPartComponent implements OnInit {    
  11.   
  12.   @Input()  
  13.   public set siteurl(url: string) {  
  14.     this.appService.setAPIUrl(url);  
  15.   }  
  16.   
  17.   listItems: ListItems[];  
  18.     
  19.   constructor(private appService: AppService) {  
  20.   }  
  21.   
  22.   ngOnInit() {  
  23.   
  24.     this.appService.getListItems().then(listItems => this.listItems = listItems);  
  25.   }  
  26. }  
[WebpartName].component.html
  1. <div class="col-md-8">    
  2.   <div class="card">    
  3.     <div class="card-body">    
  4.       <h3>List Items</h3>    
  5.       <table class="table">    
  6.         <thead>    
  7.           <tr>    
  8.             <th>Id</th>    
  9.             <th>Title</th>    
  10.           </tr>    
  11.         </thead>    
  12.         <tbody>    
  13.           <tr *ngFor="let listItem of listItems">    
  14.             <td scope="row">{{listItem.Id}}</td>    
  15.             <td scope="row">{{listItem.Title}}</td>    
  16.           </tr>    
  17.         </tbody>    
  18.       </table>    
  19.     </div>    
  20.   </div>    
  21. </div>    
Bundle Angular Elements
 
To use angular elements in SPFx webpart we need to bundle it in a js file so for this we will execute the below command 
  1. npm run bundle
This command generates Angular application bundles and styles.css and after that combines all the JS files into a bundle.js file.
 
Using SPFx Webpart With Angular Elements 
 
Now we will import styles.css file in our spfx webpart after bundle.js statement in our webpart.ts file as below
  1. require('../../../node_modules/angular-elements/dist/AngularElements/styles.css');  
and then we will update the render method.
 
Here <app-sp-fx-angular9-web-part> is the custom element, which we have created with Angular. We will add this custom element tag in DOM to render Angular element in the webpart.
  1. public render(): void {  
  2.     const siteUrl = this.context.pageContext.web.absoluteUrl;  
  3.     this.domElement.innerHTML = `<app-sp-fx-angular9-web-part siteUrl="${siteUrl}"></app-sp-fx-angular9-web-part>`;  
  4.   }  
Now we will test it on a local server so for this we will bundle the Angular project and then run spfx webpart as below
How to run SPFx webpart
  • Our application will serve on SharePoint-SiteURL + /_layouts/15/workbench.aspx page.
  • After this is successfully served on localhost we will deploy it in our SharePoint site using gulp bundle --ship and gulp package-solution --ship commands.
Output 
 
Output
 
Find the complete source code here
 

Summary 

 
In this article, we have seen how to integrate SPFx webpart with Angular elements with pre-requisites, environment setup, development, and deployment and demo application.
  
I hope this helps.
 
Sharing is caring!!!