SharePoint With Angular - Part Two

Introduction 

 
This article contains the remaining activities of our SharePoint with an Angular project and it is a continuation of my previous article SharePoint With Angular - Part One.
 
Prerequisites
  • Visual Studio Code. You can download and install from Visual Studio Code
  • Node.js. You can download and install from Node.js
  • Basic knowledge of SharePoint
  • Knowledge of TypeScript
Note
The remaining activities of our Sharepoint projects using Angular are:
  • Create an angular project and write the code for CRUD operation in SharePoint.
  • Integrate third-party library (PrimeNg) for showing records.
  • Build and add an Angular project to the SharePoint site.

Create an Angular project and write the code for a CRUD operation in Sharepoint

 
To demonstrate the CRUD operation in SharePoint online, I have created the Employee list with following the columns. 
 
 
Let's create an Angular project using Angular CLI. You can install angular CLI using npm install -g @angular/cli
 
Step 1
 
Open the command prompt and type below command to create an Angular project where SharepointWithAngular is the project name.
 
ng new SharepointWithAngular
 
Step 2
 
Select the following options.
 
Would you like to add Angular routing? - y (Type y, as we are using routing for SharepointWithAngular).
 
Which stylesheet format would you like to use? CSS (You can choose any option, As I am using CSS for this project).
 
Now it will install the necessary packages and files. Once done, open the proxy-server project in Visual Studio code.
 
 
Step 3
 
Now install the proxy library by using the following command.
 
npm install sp-rest-proxy --save-dev - To install sp-rest-proxy library.
 
Step 4
 
Create a proxy.conf.json file and write the below code.
  1. {  
  2.     "/sites/*": {  
  3.         "target""http://localhost:8080",  
  4.         "secure"false,  
  5.         "changeOrigin"true,  
  6.         "logLevel""debug",  
  7.         "pathRewrite": {  
  8.             "^/sites""http://localhost:8080/sites"  
  9.         }  
  10.     }  
  11. }  
Modify the start tag with this line in package.json file under the scripts tag.
 
"start": "ng serve --proxy-config proxy.conf.json" 
 
 
Note
Step(3 and 4) are required so that we can use SharePoint services(like REST API, Groups, list, etc) locally or on localhost by running the command.
 
npm run start 
 
Step 5
 
In order to perform actions like create, view, update, and delete in Sharepoint list item. Create the following services with these commands.
 
ng g s services/Constant - where (g - generate and s - service)
 
It will generate two files constant.service.ts and constant.service.spec.ts(It is used for testing). Open constant.services.ts file and paste the following code in it.
  1. import { Injectable } from '@angular/core';  
  2.   
  3. @Injectable({  
  4.   providedIn: 'root'  
  5. })  
  6. export class ConstantService {  
  7.   
  8.   constructor() { }  
  9.   // Define constant for list names  
  10.   public listNames = {  
  11.     Employee: {  
  12.       name: 'Employee',  
  13.       type: 'SP.Data.EmployeeListItem'  
  14.     }  
  15.   }  
  16.   // Define REST API Query in Constant file.  
  17.   public QUERY = {  
  18.     GET_CHOICEFIELD: {  
  19.       filter: 'EntityPropertyName eq \'{{choiceField}}\''  
  20.     },  
  21.     GET_EMPLOYEE: {  
  22.       select: 'ID,Title,FirstName,LastName,Address,Role,IsActive,PhoneNo,Created,Modified,Editor/ID,Editor/Title',  
  23.       expand: 'Editor',  
  24.       filter: 'IsActive eq \'{{isActive}}\''  
  25.   
  26.     }  
  27.   }  
  28. }  
ng g s services/Common
 
It will generate two files common.service.ts and common.service.spec.ts(It is used for testing). Opencommon.services.tsfile and paste the following code in it.
  1. import { Injectable } from '@angular/core';  
  2. import { DatePipe } from '@angular/common';  
  3.   
  4. @Injectable({  
  5.   providedIn: 'root'  
  6. })  
  7. export class CommonService {  
  8.   constructor(  
  9.     private datePipe: DatePipe  
  10.   ) { }  
  11.   /** 
  12.  * construct a request to filter the array data into unique records. 
  13.  * 
  14.  * @description 
  15.  * 
  16.  * It will filter the array with unique records and return the filter array. 
  17.  * 
  18.  * @param array The array parameters contains an array which is required to filter the data. 
  19.  * 
  20.  * @return array It will returns an array having a unique value into the array. 
  21.  */  
  22.   uniqueArrayObj(array: any) {  
  23.     let sts: any = '';  
  24.     return sts = Array.from(new Set(array.map(s => s.label))).map(label1 => {  
  25.       return {  
  26.         label: label1,  
  27.         value: array.find(s => s.label === label1).value  
  28.       };  
  29.     });  
  30.   }  
  31.   // Sort array with property as label & value  
  32.   
  33.   sortData(array: any) {  
  34.     return array.sort((a, b) => {  
  35.       if (a.label && a.label !== null && b.label && b.label !== null) {  
  36.         if (a.label.toLowerCase() < b.label.toLowerCase()) {  
  37.           return -1;  
  38.         }  
  39.         if (a.label.toLowerCase() > b.label.toLowerCase()) {  
  40.           return 1;  
  41.         }  
  42.       }  
  43.       return 0;  
  44.     })  
  45.   }  
  46.   /** 
  47.    * This method is used to sort number. 
  48.    * @param array Pass the array as parameter. 
  49.    */  
  50.   sortNumberArray(array: any) {  
  51.     return array.sort((a, b) => {  
  52.       if (parseFloat(a.label) && parseFloat(b.label)) {  
  53.         return a.label - b.label;  
  54.       }  
  55.       return 0;  
  56.     })  
  57.   }  
  58.   /** 
  59.    * This method is used to sort the DateArray; 
  60.    * @param array Pass the array as parameter. 
  61.    */  
  62.   sortDateArray(array: any) {  
  63.     const reverseDateRepresentation = date => {  
  64.       if (date) {  
  65.         let d1 = this.datePipe.transform(date.label ? date.label : date, 'dd-MM-yyyy');  
  66.         let parts = d1.split('-');  
  67.         let sortedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;  
  68.         return sortedDate;  
  69.       } else {  
  70.         return null;  
  71.       }  
  72.     };  
  73.     const sortedDates = array  
  74.       .map(reverseDateRepresentation)  
  75.       .sort()  
  76.       .map(reverseDateRepresentation);  
  77.     return sortedDates;  
  78.   }  
  79. }  
ng g s services/SPOperation
 
It will generate two files spoperation.service.ts and spoperation.service.spec.ts(It is used for testing). Openspoperation.services.tsfile and paste the following code in it.
  1. import { Injectable } from '@angular/core';  
  2. import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';  
  3. import { ConstantService } from './constant.service';  
  4. import { GlobalObjectService } from './global-object.service';  
  5. @Injectable({  
  6.   providedIn: 'root'  
  7. })  
  8. export class SPOperationService {  
  9.   // declare the variable to define header  
  10.   jsonHeader = 'application/json; odata=verbose';  
  11.   headers = { 'Content-Type'this.jsonHeader, Accept: this.jsonHeader };  
  12.   //declare the apiUrl  
  13.   apiUrl: string;  
  14.   baseUrl: string;  
  15.   constructor(  
  16.     private httpClient: HttpClient,  
  17.     private constants: ConstantService,  
  18.     private globalObj: GlobalObjectService  
  19.   ) { this.setBaseUrl(null); }  
  20.   /** 
  21.    * This method is used to set the default base url  
  22.    * @param webUrl  
  23.    */  
  24.   setBaseUrl(webUrl?: string) {  
  25.     const ctx = window['_spPageContextInfo'];  
  26.     if (ctx) {  
  27.       // If user is on SharePoint page use _spPageContextInfo.webAbsoluteUrl  
  28.       this.baseUrl = ctx.webAbsoluteUrl;  
  29.     } else {  
  30.       // Use the local variable value  
  31.       this.baseUrl = this.globalObj.sharePointPageObject.webAbsoluteUrl;  
  32.     }  
  33.     // Default to local web URL  
  34.     this.apiUrl = this.baseUrl + '/_api/web/lists/GetByTitle(\'{0}\')/items';  
  35.   }  
  36.   /** 
  37.    * This method is used to get the headers. 
  38.    * @param bAddContext  
  39.    * @param returnOp  
  40.    */  
  41.   getHeaders(bAddContext, returnOp) {  
  42.     const headerCopy: any = Object.assign({}, this.headers);  
  43.     if (bAddContext) {  
  44.       const context: any = document.getElementById('__REQUESTDIGEST');  
  45.       if (context) {  
  46.         headerCopy['X-RequestDigest'] = context.value;  
  47.       }  
  48.     }  
  49.     if (returnOp) {  
  50.       const httpOptions = {  
  51.         headers: new HttpHeaders(headerCopy)  
  52.       };  
  53.       return httpOptions;  
  54.     } else {  
  55.       return headerCopy;  
  56.     }  
  57.   
  58.   }  
  59.   /** 
  60.    * This method is used to read the ODATA properties of sharepoint url and bind them. 
  61.    * @param url pass the url as parameters 
  62.    * @param options pass the options 
  63.    */  
  64.   readBuilder(url: string, options: any): string {  
  65.     if (options) {  
  66.       if (options.select) {  
  67.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$select=' + options.select;  
  68.       }  
  69.       if (options.filter) {  
  70.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$filter=' + options.filter;  
  71.       }  
  72.       if (options.expand) {  
  73.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$expand=' + options.expand;  
  74.       }  
  75.       if (options.orderby) {  
  76.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$orderby=' + options.orderby;  
  77.       }  
  78.       if (options.top) {  
  79.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$top=' + options.top;  
  80.       }  
  81.       if (options.skip) {  
  82.         url += ((url.indexOf('?') === -1) ? '?' : '&') + '$skip=' + options.skip;  
  83.       }  
  84.     }  
  85.     return url;  
  86.   }  
  87.   /** 
  88.    * This method is used to get the Item url for sharepoint 
  89.    * @param listName pass the listname as parameter 
  90.    * @param id pass the id for update and delete operation 
  91.    * @param options pass data in options parameter if you want to update else donot pass. 
  92.    */  
  93.   getItemURL(listName: string, id: any, options?: any) {  
  94.     let url = this.apiUrl.replace('{0}', listName) + '(' + id + ')';  
  95.     url = this.readBuilder(url, options);  
  96.     return url;  
  97.   }  
  98.   /** 
  99.    * This method is used to build the get items url 
  100.    * @param listName Pass the listname as parameter 
  101.    * @param options Pass the options as parameter. 
  102.    */  
  103.   getReadURL(listName: string, options?: any) {  
  104.     let url = this.apiUrl.replace('{0}', listName);  
  105.     url = this.readBuilder(url, options);  
  106.     return url;  
  107.   }  
  108.   /** 
  109.    * This method will create the new item in sharepoint list 
  110.    * @param listName pass the listname 
  111.    * @param jsonBody pass the body 
  112.    * @param type pass the type  
  113.    */  
  114.   async createItem(listName: string, jsonBody: any, type: string) {  
  115.     const url = this.getReadURL(listName, null);  
  116.     // append metadata  
  117.     if (!jsonBody.__metadata) {  
  118.       jsonBody.__metadata = {  
  119.         // tslint:disable-next-line:object-literal-key-quotes  
  120.         'type': type  
  121.       };  
  122.     }  
  123.     // Call the http post method  
  124.     const data = JSON.stringify(jsonBody);  
  125.     const res = await this.httpClient.post(url, data, this.getHeaders(truetrue)).toPromise().catch((err: HttpErrorResponse) => {  
  126.       const error = err.error;  
  127.       return error;  
  128.     });  
  129.     return res.d.results;  
  130.   }  
  131.   /** 
  132.    * This method is used to update the item in sharepoint list 
  133.    * @param listName pass the list name as parameter 
  134.    * @param id pass the id of the item. 
  135.    * @param jsonBody pass the data which you want to update 
  136.    * @param type pass the type of list. 
  137.    */  
  138.   async updateItem(listName: string, id: any, jsonBody: any, type?: string) {  
  139.     // Append HTTP header MERGE for UPDATE scenario  
  140.     const localOptions = this.getHeaders(truefalse);  
  141.     localOptions['X-HTTP-Method'] = 'MERGE';  
  142.     localOptions['If-Match'] = '*';  
  143.     // Append metadata  
  144.     if (!jsonBody.__metadata) {  
  145.       jsonBody.__metadata = {  
  146.         // tslint:disable-next-line:object-literal-key-quotes  
  147.         'type': type  
  148.       };  
  149.     }  
  150.     // stringify the data  
  151.     const data = JSON.stringify(jsonBody);  
  152.     const url = this.getItemURL(listName, id);  
  153.     const httpOptions = {  
  154.       headers: new HttpHeaders(localOptions)  
  155.     };  
  156.     // Call the http post method  
  157.     await this.httpClient.post(url, data, httpOptions).toPromise().catch((err: HttpErrorResponse) => {  
  158.       const error = err.error;  
  159.       return error;  
  160.     });  
  161.   }  
  162.   /** 
  163.    * This method is used to read items from sharepoint list based on REST query. 
  164.    * @param listName Pass the listname as a parameter 
  165.    * @param options pass the query 
  166.    */  
  167.   async readItems(listName: string, options?: any) {  
  168.     // build the read url  
  169.     const url = this.getReadURL(listName, options);  
  170.     let res;  
  171.     // Call the http get method  
  172.     res = await this.httpClient.get(url, this.getHeaders(truetrue)).toPromise().catch((err: HttpErrorResponse) => {  
  173.       const error = err.error;  
  174.       return error;  
  175.     });  
  176.     return res.d.results;  
  177.   }  
  178.   /** 
  179.    * This method is used to delete items from sharepoint list based on REST query. 
  180.    * @param listName Pass the listName as a parameter. 
  181.    * @param id pass the list item Id 
  182.    */  
  183.   async deleteItem(listName: string, id: number) {  
  184.     // Append HTTP header Delete for Delete scenario  
  185.     const localOptions = this.getHeaders(truefalse);  
  186.     localOptions['X-HTTP-Method'] = 'DELETE'// set the delete header for post call.  
  187.     localOptions['If-Match'] = '*';  
  188.     const url = this.getItemURL(listName, id);  
  189.     const httpOptions = {  
  190.       headers: new HttpHeaders(localOptions)  
  191.     };  
  192.     // build the delete url  
  193.     let res;  
  194.     // Call the http delete method  
  195.     res = await this.httpClient.post(url, null, httpOptions).toPromise().catch((err: HttpErrorResponse) => {  
  196.       const error = err.error;  
  197.       return error;  
  198.     });  
  199.     return res;  
  200.   }  
  201.   /** 
  202.    * This method is used to get the choice field value from list 
  203.    * @param listName Pass the list name as parameter 
  204.    * @param options Pass the optiosn as parameter 
  205.    */  
  206.   async getChoiceFieldItems(listName: string, options?: any) {  
  207.     const url = this.getChoiceFieldUrl(listName, options);  
  208.     let res;  
  209.     res = await this.httpClient.get(url, this.getHeaders(truetrue)).toPromise().catch((err: HttpErrorResponse) => {  
  210.       const error = err.error;  
  211.       return error;  
  212.     });  
  213.     return res.d.results;  
  214.   }  
  215.   
  216.   /** 
  217.    * This method is used to build the url for choice columns. 
  218.    * @param listName Pass the list name as parameter 
  219.    * @param options Pass the options as parameter. 
  220.    */  
  221.   getChoiceFieldUrl(listName: string, options?: any) {  
  222.     const choiceUrl = this.baseUrl + '/_api/web/lists/GetByTitle(\'{listName}\')/fields';  
  223.     let url = choiceUrl.replace('{listName}', listName);  
  224.     url = this.readBuilder(url, options);  
  225.     return url;  
  226.   }  
  227. }  
ng g s services/GlobalObject
 
It will generate two files global-object.service.ts and global-object.service.spec.ts (It is used for testing). Openglobal-object.services.tsfile and paste the following code in it.
  1. import { Injectable } from '@angular/core';  
  2.   
  3. @Injectable({  
  4.   providedIn: 'root'  
  5. })  
  6. export class GlobalObjectService {  
  7.   constructor() { }  
  8.   //Define the SharePoint Object.  
  9.   public sharePointPageObject = {  
  10.     webAbsoluteUrl: '',  
  11.     webRelativeUrl: '',  
  12.     userId: 0  
  13.   };  
  14. }  
Step 6
 
Write this code in the following files which are required for our projects.
 
app.component.ts 
  1. import { Component, ViewEncapsulation, NgZone } from '@angular/core';  
  2. import { GlobalObjectService } from './services/global-object.service';  
  3. import { Router } from '@angular/router';  
  4. declare const _spPageContextInfo;  
  5.   
  6. @Component({  
  7.   selector: 'app-root',  
  8.   templateUrl: './app.component.html',  
  9.   styleUrls: ['./app.component.css'],  
  10.   encapsulation: ViewEncapsulation.None  
  11. })  
  12. export class AppComponent {  
  13.   constructor(  
  14.     private globalService: GlobalObjectService,  
  15.     private router: Router,  
  16.     private _ngZone: NgZone  
  17.   ) { }  
  18.   /** 
  19.    * This method will initialize the required variable for project 
  20.    */  
  21.   ngOnInit() {  
  22.     // Set the webAbsoluteUrl for localhost and sharepoint page  
  23.     this.globalService.sharePointPageObject.webAbsoluteUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'  
  24.       : _spPageContextInfo.webAbsoluteUrl;  
  25.     this.globalService.sharePointPageObject.webRelativeUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'  
  26.       : _spPageContextInfo.webRelativeUrl;  
  27.     this.globalService.sharePointPageObject.userId = window.location.href.indexOf('localhost') > -1 ? 'Your User Id': _spPageContextInfo.userId;  
  28.     // This will call first when employee page loads  
  29.     window['employeeComponentReference'] = {  
  30.       component: this, zone: this._ngZone,  
  31.       loadEmployeeDashboard: () => this.goToEmployeeDashboard(),  
  32.     };  
  33.   }  
  34.   // This method set the routing first time.  
  35.   goToEmployeeDashboard() {  
  36.     if (window.location.href.includes('employee') || !window.location.href.includes('#')) {  
  37.       this.router.navigate(['/employee']);  
  38.     }  
  39.   }  
  40. }  
app.component.html
  1. <div>  
  2.   <a routerLink="/employee">open employee section</a>  
  3. </div>  
  4. <router-outlet></router-outlet>  
app.component.css
  1. @import '~primeicons/primeicons.css';  
  2. body {  
  3.     font-familyHelvetica;  
  4.     font-size14px;  
  5. }  
  6. .empTable div.ui-table-wrapper {  
  7.     overflowvisible!important;  
  8. }  
  9.   
  10. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child {  
  11.     overflowvisible;  
  12. }  
  13.   
  14. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div {  
  15.     positionrelative;  
  16. }  
  17.   
  18. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div>i {  
  19.     top: 0px !important;  
  20. }  
  21.   
  22. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div div.ui-menu {  
  23.     top: 0px !important;  
  24.     left: inherit !important;  
  25.     right: 40px !important;  
  26. }  
  27. .minFirstLastCol5Per .ui-table .ui-table-thead > tr> th:first-child,  
  28. .minFirstLastCol5Per .ui-table .ui-table-thead > tr> th:last-child,  
  29. .minFirstLastCol5Per .ui-table .ui-table-tbody > tr> td:first-child,  
  30. .minFirstLastCol5Per .ui-table .ui-table-tbody > tr> td:last-child {  
  31.  width5% !important;  
  32. }   
  33. .minFirstLastCol5Per .ui-table .ui-table-tbody tr> td{  
  34.     word-break: break-all;  
  35. }  
  36. .clearBoth {  
  37.     clearboth;  
  38. }  
  39. .compulsory {  
  40.     displayinline;  
  41.     padding-right2px;  
  42.     color#c53e3e;  
  43. }  
  44. .mr-5 {  
  45.     margin-right5px;  
  46. }  
  47.   
  48. .mt-20 {  
  49.     margin-top20px;  
  50. }  
  51. input,  
  52. optgroup,  
  53. select,  
  54. textarea {  
  55.     padding6px 20px 6px 6px !important;  
  56. }  
  57. .error {  
  58.     colorred !important;  
  59. }  
  60.   
  61. .spanner {  
  62.     positionabsolute;  
  63.     /* top: calc( 100vh - 35vh); */  
  64.     padding50% 0% 35% 0%;  
  65.     left: 0;  
  66.     background#2a2a2a55;  
  67.     width100%;  
  68.     displayblock;  
  69.     text-aligncenter;  
  70.     color#FFF;  
  71.     transform: translateY(-50%);  
  72.     z-index1000;  
  73.     visibilityhidden;  
  74. }  
  75.   
  76. .overlay {  
  77.     positionfixed;  
  78.     width100%;  
  79.     height100%;  
  80.     visibilityhidden;  
  81. }  
  82.   
  83. .spanner,  
  84. .overlay {  
  85.     opacity: 0;  
  86.     -webkit-transition: all 0.3s;  
  87.     -moz-transition: all 0.3s;  
  88.     transition: all 0.3s;  
  89. }  
  90.   
  91. .spanner.show,  
  92. .overlay.show {  
  93.     opacity: 1  
  94. }  
  95. .show {  
  96.     visibilityvisible;  
  97. }  
  98.   
  99. .loader,  
  100. .loader:before,  
  101. .loader:after {  
  102.     border-radius: 50%;  
  103.     width2.5em;  
  104.     height2.5em;  
  105.     -webkit-animation-fill-mode: both;  
  106.     animation-fill-mode: both;  
  107.     -webkit-animation: load7 1.8s infinite ease-in-out;  
  108.     animation: load7 1.8s infinite ease-in-out;  
  109. }  
  110.   
  111. .loader {  
  112.     color#ffffff;  
  113.     font-size10px;  
  114.     margin30px auto;  
  115.     positionrelative;  
  116.     text-indent-9999em;  
  117.     -webkit-transform: translateZ(0);  
  118.     -ms-transform: translateZ(0);  
  119.     transform: translateZ(0);  
  120.     -webkit-animation-delay: -0.16s;  
  121.     animation-delay: -0.16s;  
  122.     margin-top20%;  
  123. }  
  124.   
  125. .loader:before,  
  126. .loader:after {  
  127.     content'';  
  128.     positionabsolute;  
  129.     top: 0;  
  130. }  
  131.   
  132. .loader:before {  
  133.     left: -3.5em;  
  134.     -webkit-animation-delay: -0.32s;  
  135.     animation-delay: -0.32s;  
  136. }  
  137.   
  138. .loader:after {  
  139.     left: 3.5em;  
  140. }  
  141.   
  142. @-webkit-keyframes load7 {  
  143.     0%,  
  144.     80%,  
  145.     100% {  
  146.         box-shadow: 0 2.5em 0 -1.3em;  
  147.     }  
  148.     40% {  
  149.         box-shadow: 0 2.5em 0 0;  
  150.     }  
  151. }  
  152.   
  153. @keyframes load7 {  
  154.     0%,  
  155.     80%,  
  156.     100% {  
  157.         box-shadow: 0 2.5em 0 -1.3em;  
  158.     }  
  159.     40% {  
  160.         box-shadow: 0 2.5em 0 0;  
  161.     }  
  162. }  
 app-routing.module.ts
  1. import { NgModule } from '@angular/core';  
  2. import { Routes, RouterModule } from '@angular/router';  
  3. import { EmployeeComponent } from './employee/employee.component';  
  4.   
  5. // Declare the route with following code  
  6. const routes: Routes = [  
  7.   { path: '', redirectTo: 'employee', pathMatch: 'full' },  
  8.   { path: 'employee', component: EmployeeComponent, pathMatch: 'full' }  
  9. ];  
  10.   
  11. @NgModule({  
  12.   imports: [RouterModule.forRoot(routes, { useHash: true, onSameUrlNavigation: 'reload' })],  
  13.   exports: [RouterModule]  
  14. })  
  15.   
  16. // export the AppRouting module.  
  17. export class AppRoutingModule { }  

Integrate third party library (PrimeNg) for showing records

 
We can easily add a third-party module or framework using npm commands. 
 
Step 1 
 
To add third party (primeng) in our project use the following command at the project directory(D:/Arvind/Solution/SharePointWithAngular)
 
npm install primeng --save - To install primeng framework
npm install primeicons --save - To install primeng icons.
npm install @angular/cdk --save - Virtual Scrolling for NgPrime dropdown depends on this library.
 
After installing primeng successfully, add the following CSS in angular.json file under styles tag to use primeng, CSS and icons.
 
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeng/resources/primeng.min.css",
"node_modules/primeflex/primeflex.css",
"node_modules/primeicons/primeicons.css"
 
 
Step 2 
 
Using Angular CLI, create the angular component(Employee) with this command.
 
ng g c Employee where (g - generate and c-component) 
 
Create one interface folder at src/app level and create employeeInterfaces.ts file under interface folder with the below code. 
  1. export interface IEmployee {  
  2.     ID: number;  
  3.     FullName: String;  
  4.     FirstName: String;  
  5.     LastName: String;  
  6.     PhoneNo: number;  
  7.     Address: String;  
  8.     Role: String;  
  9.     IsActive: String;  
  10.     Created: Date;  
  11.     Modified: Date;  
  12.     ModifiedID: number;  
  13.     ModifiedBy: String;  
  14. }  
Step 3 
 
To use various ngprime component such as table(p-table), dropdown(p-dropdown), multiselect(p-multiselect), sidebar(p-sidebar) etc, import these files into app.module.ts files.
  1. import { MultiSelectModule } from 'primeng/multiselect';  
  2. import {SidebarModule} from 'primeng/sidebar';  
  3. import { DynamicDialogModule, DialogService } from 'primeng/dynamicdialog';  
  4. import { ToastModule } from 'primeng/toast';  
  5. import { DialogModule } from 'primeng/dialog';  
  6. import { ButtonModule } from 'primeng/button';  
  7. import {ConfirmDialogModule} from 'primeng/confirmdialog';  
  8. import { MenuModule } from 'primeng/menu';  
  9. import {TableModule} from 'primeng/table';  
  10. import {DropdownModule} from 'primeng/dropdown';  
  11. import { BrowserAnimationsModule } from '@angular/platform-browser/animations';  
  12. import { HttpClientModule } from '@angular/common/http';  
  13. import { DatePipe } from '@angular/common';  
  14. import { MessageService, ConfirmationService } from 'primeng/api';  
Complete app.module.ts
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3. import { FormsModule, ReactiveFormsModule } from "@angular/forms"  
  4. import { AppRoutingModule } from './app-routing.module';  
  5. import { AppComponent } from './app.component';  
  6. import { EmployeeComponent } from './employee/employee.component';  
  7. import { MultiSelectModule } from 'primeng/multiselect';  
  8. import {SidebarModule} from 'primeng/sidebar';  
  9. import { DynamicDialogModule, DialogService } from 'primeng/dynamicdialog';  
  10. import { ToastModule } from 'primeng/toast';  
  11. import { DialogModule } from 'primeng/dialog';  
  12. import { ButtonModule } from 'primeng/button';  
  13. import {ConfirmDialogModule} from 'primeng/confirmdialog';  
  14. import { MenuModule } from 'primeng/menu';  
  15. import {TableModule} from 'primeng/table';  
  16. import {DropdownModule} from 'primeng/dropdown';  
  17. import { BrowserAnimationsModule } from '@angular/platform-browser/animations';  
  18. import { HttpClientModule } from '@angular/common/http';  
  19. import { DatePipe } from '@angular/common';  
  20. import { MessageService, ConfirmationService } from 'primeng/api';  
  21. @NgModule({  
  22.   declarations: [  
  23.     AppComponent,  
  24.     EmployeeComponent  
  25.   ],  
  26.   imports: [  
  27.     BrowserModule,  
  28.     AppRoutingModule,  
  29.     MultiSelectModule,  
  30.     DynamicDialogModule,  
  31.     ToastModule,  
  32.     DialogModule,  
  33.     ButtonModule,  
  34.     MenuModule,  
  35.     FormsModule,  
  36.     TableModule,  
  37.     SidebarModule,  
  38.     DropdownModule,  
  39.     ReactiveFormsModule,  
  40.     BrowserAnimationsModule,  
  41.     HttpClientModule,  
  42.     ConfirmDialogModule  
  43.   ],  
  44.   providers: [DialogService, DatePipe, MessageService, ConfirmationService],  
  45.   bootstrap: [AppComponent]  
  46. })  
  47. export class AppModule { }  
Step 4
 
Add the below code in employee component (Employee component generates these four files).
 
employee.html
  1. <div class="mt-20">  
  2.     <button pButton icon="pi pi-plus-circle" label="Add New Employee" type="button"  
  3.         (click)="showAddEditEmployeeForm()"></button>  
  4. </div>  
  5. <div class="clearBoth"></div>  
  6. <div #loader class="overlay"></div>  
  7. <div #spanner class="spanner">  
  8.     <div class="loader"></div>  
  9.     <p>Please wait...</p>  
  10. </div>  
  11. <div class="empTable mt-20">  
  12.     <div *ngIf="showTable">  
  13.         <p-table #up [columns]="employeeColumns" [value]="employeeDataArray" [paginator]="true" [rows]="10"  
  14.             class="minFirstLastCol5Per">  
  15.             <ng-template pTemplate="header" let-columns>  
  16.                 <tr>  
  17.                     <th>Sr.No.</th>  
  18.                     <th *ngFor="let col of columns" [pSortableColumn]="col.field" [hidden]="!col.visibility">  
  19.                         {{col.header}}  
  20.                         <p-sortIcon [field]="col.field" ariaLabel="Activate to sort"  
  21.                             ariaLabelDesc="Activate to sort in descending order"  
  22.                             ariaLabelAsc="Activate to sort in ascending order">  
  23.                         </p-sortIcon>  
  24.                     </th>  
  25.                     <th></th>  
  26.                 </tr>  
  27.                 <tr>  
  28.                     <th></th>  
  29.                     <th *ngFor="let col of columns" [ngSwitch]="col.field" [hidden]="!col.visibility">  
  30.                         <p-multiSelect *ngSwitchCase="'FirstName'" [style]="{'width':'100%'}"  
  31.                             [options]="employeeMultiColArray.firstNameArray"  
  32.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  33.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  34.                             (onPanelShow)="isOptionFilter=true;">  
  35.                         </p-multiSelect>  
  36.                         <p-multiSelect *ngSwitchCase="'LastName'" [style]="{'width':'100%'}"  
  37.                             [options]="employeeMultiColArray.lastNameArray"  
  38.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  39.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  40.                             (onPanelShow)="isOptionFilter=true;">  
  41.                         </p-multiSelect>  
  42.                         <p-multiSelect *ngSwitchCase="'FullName'" [style]="{'width':'100%'}"  
  43.                             [options]="employeeMultiColArray.fullNameArray"  
  44.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  45.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  46.                             (onPanelShow)="isOptionFilter=true;">  
  47.                         </p-multiSelect>  
  48.                         <p-multiSelect *ngSwitchCase="'PhoneNo'" [style]="{'width':'100%'}"  
  49.                             [options]="employeeMultiColArray.phoneNoArray"  
  50.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  51.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  52.                             (onPanelShow)="isOptionFilter=true;">  
  53.                         </p-multiSelect>  
  54.                         <p-multiSelect *ngSwitchCase="'Address'" [style]="{'width':'100%'}"  
  55.                             [options]="employeeMultiColArray.addressArray"  
  56.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  57.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  58.                             (onPanelShow)="isOptionFilter=true;">  
  59.                         </p-multiSelect>  
  60.                         <p-multiSelect *ngSwitchCase="'Role'" [style]="{'width':'100%'}"  
  61.                             [options]="employeeMultiColArray.roleArray"  
  62.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  63.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  64.                             (onPanelShow)="isOptionFilter=true;">  
  65.                         </p-multiSelect>  
  66.                         <p-multiSelect *ngSwitchCase="'IsActive'" [style]="{'width':'100%'}"  
  67.                             [options]="employeeMultiColArray.isActiveArray"  
  68.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  69.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  70.                             (onPanelShow)="isOptionFilter=true;">  
  71.                         </p-multiSelect>  
  72.                         <p-multiSelect *ngSwitchCase="'Modified'" [style]="{'width':'100%'}"  
  73.                             [options]="employeeMultiColArray.lastUpdatedArray"  
  74.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  75.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  76.                             (onPanelShow)="isOptionFilter=true;">  
  77.                         </p-multiSelect>  
  78.                         <p-multiSelect *ngSwitchCase="'ModifiedBy'" [style]="{'width':'100%'}"  
  79.                             [options]="employeeMultiColArray.lastModifiedByArray"  
  80.                             (onChange)="up.filter($event.value, col.field, 'in')"  
  81.                             (keydown.enter)="$event.preventDefault()" filter="true" resetFilterOnHide="true"  
  82.                             (onPanelShow)="isOptionFilter=true;">  
  83.                         </p-multiSelect>  
  84.                     </th>  
  85.                     <th></th>  
  86.                 </tr>  
  87.             </ng-template>  
  88.             <ng-template pTemplate="body" let-rowData let-columns="columns" let-rowIndex="rowIndex">  
  89.                 <tr>  
  90.                     <td>  
  91.                         {{rowIndex+1}}  
  92.                     </td>  
  93.                     <td *ngFor="let col of columns" [hidden]="!col.visibility" class="ui-resizable-column">  
  94.                         <ng-container *ngIf="col.field != 'Created' && col.field != 'Modified' &&  col.field != '' ">  
  95.                             {{rowData[col.field]}}  
  96.                         </ng-container>  
  97.                         <ng-container *ngIf="col.field == 'Modified'">  
  98.                             {{rowData[col.field] | date:'MMM dd, yyyy' }}  
  99.                         </ng-container>  
  100.                     </td>  
  101.                     <td>  
  102.                         <div>  
  103.                             <p-menu #menu [popup]="true" [model]="pMenuItems" styleClass="borderMenu"></p-menu>  
  104.                             <i class="pi pi-ellipsis-v" style="font-size:2em;top: 100px !important;cursor: pointer;"  
  105.                                 (click)="storeRowData(rowData); menu.toggle($event);"></i>  
  106.                         </div>  
  107.                     </td>  
  108.                 </tr>  
  109.             </ng-template>  
  110.             <ng-template pTemplate="emptymessage" let-columns>  
  111.                 <tr>  
  112.                     <td [attr.colspan]="9">  
  113.                         No Data found  
  114.                     </td>  
  115.                 </tr>  
  116.             </ng-template>  
  117.         </p-table>  
  118.     </div>  
  119.     <div *ngIf="!employeeDataArray.length">  
  120.         <span>No employee data found.</span>  
  121.     </div>  
  122. </div>  
  123. <div class="clearBoth"></div>  
  124. <p-dialog [(visible)]="isEmployeeFormVisible" [modal]="true" appendTo="body" [style]="{width: '1000px'}">  
  125.     <p-header *ngIf="isAddEmployee">  
  126.         Add Employee  
  127.     </p-header>  
  128.     <p-header *ngIf="!isAddEmployee">  
  129.         Edit Employee  
  130.     </p-header>  
  131.     <form [formGroup]='employeeForm'>  
  132.         <div class="p-grid mt-20">  
  133.             <div class="p-col-2 "><span class="compulsory">*</span>First Name</div>  
  134.             <div class="p-col-4">  
  135.                 <input class="mr-5" type="text" size="30" formControlName="firstName" placeholder="First Name"  
  136.                     pInputText>  
  137.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.firstName.errors">  
  138.                     <div *ngIf="employeeForm.controls.firstName.errors.required">  
  139.                         First Name is required</div>  
  140.                 </div>  
  141.             </div>  
  142.             <div class="p-col-2 "><span class="compulsory">*</span>Last Name</div>  
  143.             <div class="p-col-4">  
  144.                 <input class="mr-5" type="text" size="30" formControlName="lastName" placeholder="Last Name" pInputText>  
  145.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.lastName.errors">  
  146.                     <div *ngIf="employeeForm.controls.lastName.errors.required">  
  147.                         Last Name is required</div>  
  148.                 </div>  
  149.             </div>  
  150.         </div>  
  151.         <div class="p-grid mt-20">  
  152.             <div class="p-col-2 "><span class="compulsory">*</span>Phone Number</div>  
  153.             <div class="p-col-4">  
  154.                 <input class="mr-5" type="number" size="30" formControlName="phoneNum" placeholder="Phone Number"  
  155.                     pInputText>  
  156.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.phoneNum.errors">  
  157.                     <div *ngIf="employeeForm.controls.phoneNum.errors.required">Phone Number is required</div>  
  158.                 </div>  
  159.             </div>  
  160.             <div class="p-col-2 "><span class="compulsory">*</span>Address</div>  
  161.             <div class="p-col-4">  
  162.                 <textarea [rows]="5" [cols]="40" formControlName="address" pInputTextarea  
  163.                     autoResize="autoResize"></textarea>  
  164.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.address.errors">  
  165.                     <div *ngIf="employeeForm.controls.address.errors.required">  
  166.                         Address is required</div>  
  167.                 </div>  
  168.             </div>  
  169.         </div>  
  170.         <div class="p-grid mt-20">  
  171.             <div class="p-col-2 "><span class="compulsory">*</span>Role</div>  
  172.             <div class="p-col-4">  
  173.                 <p-dropdown [options]="roleDropArray" placeholder="Select Role" formControlName="role" filter="true"  
  174.                     resetFilterOnHide="true" showIcon="true">  
  175.                 </p-dropdown>  
  176.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.role.errors">  
  177.                     <div *ngIf="employeeForm.controls.role.errors.required">  
  178.                         Role is required</div>  
  179.                 </div>  
  180.             </div>  
  181.             <div class="p-col-2 "><span class="compulsory">*</span>Is Active</div>  
  182.             <div class="p-col-4">  
  183.                 <p-dropdown [options]="isActiveDropArray" placeholder="Select Active" formControlName="isActive"  
  184.                     filter="true" resetFilterOnHide="true" showIcon="true">  
  185.                 </p-dropdown>  
  186.                 <div class="error" *ngIf="isEmployeeFormSubmit && employeeForm.controls.isActive.errors">  
  187.                     <div *ngIf="employeeForm.controls.isActive.errors.required">  
  188.                         Is Active is required</div>  
  189.                 </div>  
  190.             </div>  
  191.         </div>  
  192.     </form>  
  193.     <p-footer>  
  194.         <button pButton label="{{isAddEmployee ? 'Add Employee': 'Edit Empoyee'}}" type="button" class="adminBtn"  
  195.             (click)="saveEmployee()"></button>  
  196.         <button pButton label="Cancel" type="button" class="adminBtn" (click)="cancelEmployee()"></button>  
  197.     </p-footer>  
  198. </p-dialog>  
  199. <div *ngIf="isEmployeeSideVisible" class="ng-right-side-overlay">  
  200.     <p-sidebar [(visible)]="isEmployeeSideVisible" position="right" styleClass="ui-sidebar-md">  
  201.         <p-table [value]="employeeRightSideArray">  
  202.             <ng-template pTemplate="header">  
  203.                 <tr>  
  204.                     <th>Name</th>  
  205.                     <th>Value</th>  
  206.                 </tr>  
  207.             </ng-template>  
  208.             <ng-template pTemplate="body" let-rowData let-emp style="overflow: auto">  
  209.                 <tr>  
  210.                     <td [ngStyle]="{'color':'#d7181f'}">First Name</td>  
  211.                     <td>{{emp.FirstName}}</td>  
  212.                 </tr>  
  213.                 <tr>  
  214.                     <td [ngStyle]="{'color':'#d7181f'}">Last Name</td>  
  215.                     <td>{{emp.LastName}}</td>  
  216.                 </tr>  
  217.                 <tr>  
  218.                     <td [ngStyle]="{'color':'#d7181f'}">Full Name</td>  
  219.                     <td>{{emp.FullName}}</td>  
  220.                 </tr>  
  221.                 <tr>  
  222.                     <td [ngStyle]="{'color':'#d7181f'}">Phone Number</td>  
  223.                     <td>{{emp.PhoneNo}}</td>  
  224.                 </tr>  
  225.                 <tr>  
  226.                     <td [ngStyle]="{'color':'#d7181f'}">Address</td>  
  227.                     <td>{{emp.Address}}</td>  
  228.                 </tr>  
  229.                 <tr>  
  230.                     <td [ngStyle]="{'color':'#d7181f'}">Role</td>  
  231.                     <td>{{emp.Role}}</td>  
  232.                 </tr>  
  233.                 <tr>  
  234.                     <td [ngStyle]="{'color':'#d7181f'}">IsActive</td>  
  235.                     <td>{{emp.IsActive}}</td>  
  236.                 </tr>  
  237.                 <tr>  
  238.                     <td [ngStyle]="{'color':'#d7181f'}">Last Modified Date</td>  
  239.                     <td>{{emp.Modified | date:'MMM dd, yyyy'}}</td>  
  240.                 </tr>  
  241.                 <tr>  
  242.                     <td [ngStyle]="{'color':'#d7181f'}">Last Modified By</td>  
  243.                     <td>{{emp.ModifiedBy}}</td>  
  244.                 </tr>  
  245.             </ng-template>  
  246.         </p-table>  
  247.     </p-sidebar>  
  248. </div>  
  249. <p-toast position="bottom-center" key="employeeToast"></p-toast>  
  250. <p-confirmDialog key="confirm"></p-confirmDialog>  
employee.component.ts 
  1. import { Component, OnInit, ViewEncapsulation, ViewChild, ElementRef } from '@angular/core';  
  2. import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';  
  3. import { IEmployee } from '../interface/empoyeeInterfaces';  
  4. import { ConstantService } from '../services/constant.service';  
  5. import { SPOperationService } from '../services/spoperation.service';  
  6. import { MessageService, ConfirmationService } from 'primeng/api';  
  7. import { DatePipe } from '@angular/common';  
  8. import { CommonService } from '../services/common.service';  
  9. @Component({  
  10.   selector: 'app-employee',  
  11.   templateUrl: './employee.component.html',  
  12.   styleUrls: ['./employee.component.css'],  
  13.   encapsulation: ViewEncapsulation.None // it will apply the parent css to this component.  
  14. })  
  15. export class EmployeeComponent implements OnInit {  
  16.   // declare variable  
  17.   showTable: boolean;  
  18.   isOptionFilter: boolean;  
  19.   isEmployeeFormSubmit: boolean = false;  
  20.   isAddEmployee: boolean;  
  21.   isEmployeeFormVisible: boolean;  
  22.   currEmployeeObj: IEmployee;  
  23.   employeeRightSideArray = [];  
  24.   isEmployeeSideVisible: boolean = false;  
  25.   // declare pmenu item which show menu in table  
  26.   pMenuItems = [];  
  27.   // create different array for multiselect.  
  28.   employeeMultiColArray = {  
  29.     firstNameArray: [],  
  30.     lastNameArray: [],  
  31.     fullNameArray: [],  
  32.     phoneNoArray: [],  
  33.     addressArray: [],  
  34.     roleArray: [],  
  35.     isActiveArray: [],  
  36.     lastUpdatedArray: [],  
  37.     lastModifiedByArray: []  
  38.   };  
  39.   // create employee coloumn array  
  40.   employeeColumns = [];  
  41.   // create employee Array  
  42.   employeeDataArray = [];  
  43.   // create role dropdown array  
  44.   roleDropArray = [];  
  45.   // create Is Active dropdown array.  
  46.   isActiveDropArray = [];  
  47.   // create form variable.  
  48.   employeeForm: FormGroup;  
  49.   
  50.   @ViewChild("loader", { staticfalse }) loaderView: ElementRef;  
  51.   @ViewChild("spanner", { staticfalse }) spannerView: ElementRef;  
  52.   /** 
  53.    * The constructor will create the object of following classes. 
  54.    * @param frmbuilder  
  55.    * @param constant  
  56.    * @param spServices  
  57.    */  
  58.   constructor(  
  59.     private frmbuilder: FormBuilder,  
  60.     private constant: ConstantService,  
  61.     private spServices: SPOperationService,  
  62.     private messageService: MessageService,  
  63.     private datepipe: DatePipe,  
  64.     private common: CommonService,  
  65.     private confirmationService: ConfirmationService  
  66.   ) { }  
  67.   
  68.   ngOnInit(): void {  
  69.     this.showTable = false;  
  70.     this.isEmployeeFormVisible = false;  
  71.     // Initialize the employee form.  
  72.     this.initEmployeeForm();  
  73.     // put the value into employee column array.  
  74.     this.employeeColumns = [  
  75.       { field: 'FirstName', header: 'First Name', visibility: true },  
  76.       { field: 'LastName', header: 'Last Name', visibility: true },  
  77.       { field: 'FullName', header: 'Full Name', visibility: true },  
  78.       { field: 'PhoneNo', header: 'PhoneNo Number', visibility: true },  
  79.       { field: 'Address', header: 'Address', visibility: true },  
  80.       { field: 'Role', header: 'Role', visibility: true },  
  81.       { field: 'IsActive', header: 'IsActive', visibility: true },  
  82.       { field: 'Modified', header: 'Last Updated Date', visibility: true },  
  83.       { field: 'ModifiedBy', header: 'Last Updated By', visibility: true },  
  84.     ];  
  85.     this.loadEmployeeTable();  
  86.   }  
  87.   /** 
  88.    * This method is used to get the value from employee list. 
  89.    * and pass that store that value into employeeDataArray = []; 
  90.    */  
  91.   async loadEmployeeTable() {  
  92.     this.isEmployeeFormVisible = false;  
  93.     this.employeeDataArray = [];  
  94.     // Prepare the Get query  
  95.     const getEmployeeOptions = Object.assign({}, this.constant.QUERY.GET_EMPLOYEE);  
  96.     getEmployeeOptions.filter = getEmployeeOptions.filter.replace(/{{isActive}}/gi, 'Yes');  
  97.     // Call the SharePoint Rest GET  
  98.     const results = await this.spServices.readItems(this.constant.listNames.Employee.name, getEmployeeOptions);  
  99.     // Check the data in results  
  100.     if (results && results.length) {  
  101.       const tempArray = [];  
  102.       // loop through array and store into the employeeData Array.  
  103.       results.forEach(element => {  
  104.         const employeeObj: IEmployee = {  
  105.           ID: element.ID,  
  106.           FullName: element.Title,  
  107.           FirstName: element.FirstName,  
  108.           LastName: element.LastName,  
  109.           PhoneNo: element.PhoneNo,  
  110.           Role: element.Role,  
  111.           Address: element.Address,  
  112.           IsActive: element.IsActive,  
  113.           Created: element.Created,  
  114.           Modified: new Date(this.datepipe.transform(element.Modified, 'MMM dd, yyyy')),  
  115.           ModifiedID: element.Editor.ID,  
  116.           ModifiedBy: element.Editor.Title  
  117.         }  
  118.         tempArray.push(employeeObj);  
  119.       });  
  120.       this.employeeDataArray = tempArray;  
  121.       // Assign the value to multi-select   
  122.       this.colFilterData(tempArray);  
  123.       this.showTable = true;  
  124.     }  
  125.   }  
  126.   /** 
  127.    * This method is used to uniquely assign the value to multiselect dropdown 
  128.    * @param colData  
  129.    */  
  130.   colFilterData(colData) {  
  131.     // sort and unique data assign to multiselect firstNameArray  
  132.     this.employeeMultiColArray.firstNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  133.       const b = {  
  134.         label: a.FirstName, value: a.FirstName  
  135.       };  
  136.       return b;  
  137.     })));  
  138.     // sort and unique data assign to multiselect lastNameArray  
  139.     this.employeeMultiColArray.lastNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  140.       const b = {  
  141.         label: a.LastName, value: a.LastName  
  142.       };  
  143.       return b;  
  144.     })));  
  145.     // sort and unique data assign to multiselect fullNameArray  
  146.     this.employeeMultiColArray.fullNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  147.       const b = {  
  148.         label: a.FullName, value: a.FullName  
  149.       };  
  150.       return b;  
  151.     })));  
  152.     // sort and unique data assign to multiselect phoneNoArray  
  153.     this.employeeMultiColArray.phoneNoArray = this.common.sortNumberArray(this.common.uniqueArrayObj(colData.map(a => {  
  154.       const b = {  
  155.         label: a.PhoneNo, value: a.PhoneNo  
  156.       };  
  157.       return b;  
  158.     })));  
  159.     // sort and unique data assign to multiselect roleArray  
  160.     this.employeeMultiColArray.roleArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  161.       const b = {  
  162.         label: a.Role, value: a.Role  
  163.       };  
  164.       return b;  
  165.     })));  
  166.     // sort and unique data assign to multiselect addressArray  
  167.     this.employeeMultiColArray.addressArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  168.       const b = {  
  169.         label: a.Address, value: a.Address  
  170.       };  
  171.       return b;  
  172.     })));  
  173.     // sort and unique data assign to multiselect isActiveArray  
  174.     this.employeeMultiColArray.isActiveArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  175.       const b = {  
  176.         label: a.IsActive, value: a.IsActive  
  177.       };  
  178.       return b;  
  179.     })));  
  180.   
  181.     // this.employeeMultiColArray.firstNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => { const b = { label: a.FirstName, value: a.FirstName }; return b; }).filter((ele: { label: any; }) => ele.label)));  
  182.     const modified = this.common.sortDateArray(this.common.uniqueArrayObj(colData.map(a => { const b = { label: this.datepipe.transform(a.Modified, 'MMM dd, yyyy'), value: a.Modified }; return b; }).filter((ele: { label: any; }) => ele.label)));  
  183.     this.employeeMultiColArray.lastUpdatedArray = modified.map((a: any) => { const b = { label: this.datepipe.transform(a, 'MMM dd, yyyy'), value: new Date(this.datepipe.transform(a, 'MMM dd, yyyy')) }; return b; }).filter((ele: { label: any; }) => ele.label);  
  184.   
  185.     // sort and unique data assign to multiselect lastModifiedArray  
  186.     this.employeeMultiColArray.lastModifiedByArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {  
  187.       const b = {  
  188.         label: a.ModifiedBy, value: a.ModifiedBy  
  189.       };  
  190.       return b;  
  191.     })));  
  192.   }  
  193.   optionFilter(event: any) {  
  194.     if (event.target.value) {  
  195.       this.isOptionFilter = false;  
  196.     }  
  197.   }  
  198.   storeRowData(rowData) {  
  199.     this.currEmployeeObj = rowData;  
  200.     // Add menu to pMenuItems Array  
  201.     this.pMenuItems = [  
  202.       { label: 'Edit', command: (e) => this.editEmployee(rowData) },  
  203.       { label: 'View', command: (e) => this.viewEmployee(rowData) },  
  204.       { label: 'Mark As InActive', command: (e) => this.markAsInactive() },  
  205.       { label: 'Delete', command: (e) => this.deleteEmployee() }  
  206.     ];  
  207.     console.log(rowData);  
  208.   }  
  209.   /** 
  210.    * This method is used to show the Add employee form. 
  211.    */  
  212.   showAddEditEmployeeForm() {  
  213.     // make isAddEmployee variable to true. So that add employee form will visible.  
  214.     this.loaderView.nativeElement.classList.add('show');  
  215.     this.spannerView.nativeElement.classList.add('show');  
  216.     this.employeeForm.reset();  
  217.     this.loadDropDown();  
  218.     setTimeout(() => {  
  219.       this.isAddEmployee = true;  
  220.     }, 500);  
  221.     this.isEmployeeFormVisible = true;  
  222.     this.spannerView.nativeElement.classList.remove('show');  
  223.   }  
  224.   async loadDropDown() {  
  225.     this.isActiveDropArray = [];  
  226.     this.roleDropArray = [];  
  227.     // Get the choice column role value form Employee list (Role is choice column);  
  228.     const getRoleInfo = Object.assign({}, this.constant.QUERY.GET_CHOICEFIELD);  
  229.     getRoleInfo.filter = getRoleInfo.filter.replace(/{{choiceField}}/gi, 'Role');  
  230.     // Get choice value  
  231.     const choiceResults = await this.spServices.getChoiceFieldItems(this.constant.listNames.Employee.name, getRoleInfo);  
  232.     console.log("Role Array value", choiceResults);  
  233.     if (choiceResults && choiceResults.length) {  
  234.       const tempArray = choiceResults[0].Choices.results;  
  235.       this.roleDropArray = [];  
  236.       // loop through each choice and add into roleDropdown array.  
  237.       tempArray.forEach(element => {  
  238.         this.roleDropArray.push({ label: element, value: element });  
  239.       });  
  240.     }  
  241.     // Here you can again make call to employee list to get the choice column value   
  242.     // But I have used the static value here.  
  243.     this.isActiveDropArray.push(  
  244.       { label: 'Yes', value: 'Yes' },  
  245.       { label: 'No', value: 'No' }  
  246.     );  
  247.   }  
  248.   /** 
  249.    * This method is used to edit the employee  
  250.    * @param currObj  
  251.    */  
  252.   async editEmployee(currObj) {  
  253.     this.spannerView.nativeElement.classList.add('show');  
  254.     await this.loadDropDown();  
  255.     this.employeeForm.get('firstName').setValue(currObj.FirstName);  
  256.     this.employeeForm.get('lastName').setValue(currObj.LastName);  
  257.     this.employeeForm.get('role').setValue(currObj.Role);  
  258.     this.employeeForm.get('address').setValue(currObj.Address);  
  259.     this.employeeForm.get('phoneNum').setValue(currObj.PhoneNo);  
  260.     this.employeeForm.get('isActive').setValue(currObj.IsActive);  
  261.     this.isAddEmployee = false;  
  262.     this.isEmployeeFormVisible = true;  
  263.     this.spannerView.nativeElement.classList.remove('show');  
  264.   }  
  265.   /** 
  266.    * This method is used to show the employee on right side. 
  267.    * @param currObj  
  268.    */  
  269.   viewEmployee(currObj) {  
  270.     this.isEmployeeSideVisible = true;  
  271.     this.spannerView.nativeElement.classList.add('show');  
  272.     this.employeeRightSideArray = [currObj]  
  273.     this.spannerView.nativeElement.classList.remove('show');  
  274.   }  
  275.   /** 
  276.    * This method is used to mark the current employee as Inactive 
  277.    * @param currObj Pass the current employee obj 
  278.    */  
  279.   async markAsInactive() {  
  280.     // Pop messages.  
  281.     this.confirmationService.confirm({  
  282.       message: 'Do you want to mark this record as Inactive?',  
  283.       header: 'Delete Confirmation',  
  284.       icon: 'pi pi-info-circle',  
  285.       key: 'confirm',  
  286.       accept: () => {  
  287.         this.onSubmit('InActive');  
  288.       },  
  289.     });  
  290.   }  
  291.   /** 
  292.    * This method is used to perform action based on operation parameter 
  293.    *  
  294.    * If InActive - It will update the Inactive='No' in Employee List. 
  295.    * If delete - It will delete the record from list. 
  296.    * @param operation  
  297.    */  
  298.   async onSubmit(operation) {  
  299.     this.spannerView.nativeElement.classList.add('show');  
  300.     if (operation === 'InActive') {  
  301.       // prepare the Json body  
  302.       const data = {  
  303.         __metadata: { type: this.constant.listNames.Employee.type },  
  304.         IsActive: 'No'  
  305.       };  
  306.       // call the update function  
  307.       await this.spServices.updateItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID, data, this.constant.listNames.Employee.type);  
  308.       this.messageService.add({  
  309.         key: 'employeeToast', severity: 'success', summary: 'Success message',  
  310.         detail: 'This employee marked as InActive in the list.'  
  311.       });  
  312.       await this.loadEmployeeTable();  
  313.       this.spannerView.nativeElement.classList.remove('show');  
  314.     }  
  315.     if (operation === 'delete') {  
  316.       this.spannerView.nativeElement.classList.add('show');  
  317.       // it will delete the employee record from list.  
  318.       await this.spServices.deleteItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID);  
  319.       this.messageService.add({  
  320.         key: 'employeeToast', severity: 'success', summary: 'Success message',  
  321.         detail: 'This employee deleted from list.'  
  322.       });  
  323.       await this.loadEmployeeTable();  
  324.       this.spannerView.nativeElement.classList.remove('show');  
  325.     }  
  326.   }  
  327.   /** 
  328.    * This method get called when delete option is selected form menu. 
  329.    * @param currObj pass the current employee object. 
  330.    */  
  331.   async deleteEmployee() {  
  332.     this.confirmationService.confirm({  
  333.       message: 'Do you want to delete this record?',  
  334.       header: 'Delete Confirmation',  
  335.       icon: 'pi pi-info-circle',  
  336.       key: 'confirm',  
  337.       accept: () => {  
  338.         this.onSubmit('delete');  
  339.       },  
  340.     });  
  341.   }  
  342.   /** 
  343.    * This method is used to add/update the employee information into sharepoint list. 
  344.    * If variable  contains the value as per following  
  345.    *  
  346.    * isAddEmployee = true - It will add item to list.  
  347.    * isAddEmployee = false - It will update item to list. 
  348.    *   
  349.    */  
  350.   async saveEmployee() {  
  351.     // hide the spanner.  
  352.     // this.spannerView.nativeElement.classList.remove('show');  
  353.     // Allow the form to be able to submit it.  
  354.     this.loaderView.nativeElement.classList.add('show');  
  355.     this.isEmployeeFormSubmit = true;  
  356.     //check if isAddEmployee is true or not  
  357.     if (this.isAddEmployee) { // add employee  
  358.       if (this.employeeForm.valid) {  
  359.         this.spannerView.nativeElement.classList.add('show');  
  360.         const empData = this.getEmployeeData();  
  361.         const result = this.spServices.createItem(this.constant.listNames.Employee.name, empData, this.constant.listNames.Employee.type);  
  362.         this.messageService.add({  
  363.           key: 'employeeToast', severity: 'success', summary: 'Success message',  
  364.           detail: 'Employee added into the list.'  
  365.         });  
  366.         this.isAddEmployee = false;  
  367.         await this.loadEmployeeTable();  
  368.         //this.loaderView.nativeElement.classList.remove('show');  
  369.         this.spannerView.nativeElement.classList.remove('show');  
  370.       }  
  371.     } else { // update employee  
  372.       this.spannerView.nativeElement.classList.add('show');  
  373.       const data = this.getEmployeeData();  
  374.       await this.spServices.updateItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID, data, this.constant.listNames.Employee.type);  
  375.       this.messageService.add({  
  376.         key: 'employeeToast', severity: 'success', summary: 'Success message',  
  377.         detail: 'Employee updated Successfully.'  
  378.       });  
  379.       this.isAddEmployee = false;  
  380.       await this.loadEmployeeTable();  
  381.       this.spannerView.nativeElement.classList.remove('show');  
  382.     }  
  383.   }  
  384.   cancelEmployee() {  
  385.     this.isEmployeeFormVisible = false;  
  386.     //reset the form value.  
  387.     this.employeeForm.reset();  
  388.   }  
  389.   /** 
  390.    * This method is used to get the form value and prepare the body data for creating new item in employee list 
  391.    *  
  392.    */  
  393.   getEmployeeData() {  
  394.     // Assingn the form value to local variable  
  395.     const employeeForm = this.employeeForm.value;  
  396.     // prepare the body data for post call  
  397.     const data: any = {  
  398.       __metadata: { type: this.constant.listNames.Employee.type },  
  399.       Title: employeeForm.firstName + ' ' + employeeForm.lastName,  
  400.       FirstName: employeeForm.firstName,  
  401.       LastName: employeeForm.lastName,  
  402.       PhoneNo: employeeForm.phoneNum,  
  403.       Address: employeeForm.address,  
  404.       Role: employeeForm.role,  
  405.       IsActive: employeeForm.isActive  
  406.     };  
  407.     // return the data.  
  408.     return data;  
  409.   }  
  410.   /** 
  411.    * This method is used to initialize the employee form. 
  412.    */  
  413.   initEmployeeForm() {  
  414.     this.employeeForm = this.frmbuilder.group({  
  415.       firstName: ['', [Validators.required]],  
  416.       lastName: ['', [Validators.required]],  
  417.       phoneNum: ['', Validators.required],  
  418.       address: ['', Validators.required],  
  419.       role: ['', Validators.required],  
  420.       isActive: ['', Validators.required]  
  421.     });  
  422.   }  
  423. }  
employee.component.css - I have added all the css in app.component.css file for demonstration purposes, but you can add employee specific CSS in
this file and common CSS in app.component.css.
 
employee.component.spec.ts - This file is used for unit testing. 
 
Note
Now we can run and check that our code is working properly on the development environment by running these two commands in two separate command prompt windows
OR
Both commands can run on the Visual Studio code terminal by opening the respective project in Visual Studio code. 
 
npm run serve - To start the server
 
 
npm run start - To send and get a request from the Sharepoint server. 
 
 
 
Additionally, we can debug our code in QA and Production environment (or any environment) by using the proxy setting with following changes in app.component.ts file
Depending upon your environment replace the following line and run the above two commands.
 
'Your Site Name' with your actual site name
 
'Your User Id' with your actual Id.
  1. this.globalService.sharePointPageObject.webAbsoluteUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'  
  2. : _spPageContextInfo.webAbsoluteUrl;  
  3. this.globalService.sharePointPageObject.webRelativeUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'  
  4. : _spPageContextInfo.webRelativeUrl;  
  5. this.globalService.sharePointPageObject.userId = window.location.href.indexOf('localhost') > -1 ? 'Your User Id': _spPageContextInfo.userId;  
Build and add angular project to SharePoint site
 
Once we have satisfied that our code is working properly then we can build and upload the code on different environments of Sharepoint sites.
 
Step 1 
 
Write these codes in angular.json file for building the code in different environments under configuration tag
 
Change the 'Your Angular files location url' with actual file location URL (In my case: https://SiteCollectionUrl/sites/sitename/SiteAssets/AngularDocs/employee/)
 
Note
I have created only three environment(devEmployee, qaEmployee and prodEmployee), You can add more environments to it.
  1. "configurations": {  
  2.   "production": {  
  3.     "fileReplacements": [  
  4.       {  
  5.         "replace""src/environments/environment.ts",  
  6.         "with""src/environments/environment.prod.ts"  
  7.       }  
  8.     ],  
  9.     "optimization"true,  
  10.     "outputHashing""all",  
  11.     "sourceMap"false,  
  12.     "extractCss"true,  
  13.     "namedChunks"false,  
  14.     "extractLicenses"true,  
  15.     "vendorChunk"false,  
  16.     "buildOptimizer"true,  
  17.     "budgets": [  
  18.       {  
  19.         "type""initial",  
  20.         "maximumWarning""2mb",  
  21.         "maximumError""5mb"  
  22.       },  
  23.       {  
  24.         "type""anyComponentStyle",  
  25.         "maximumWarning""6kb",  
  26.         "maximumError""10kb"  
  27.       }  
  28.     ]  
  29.   },  
  30.   "devEmployee": {  
  31.     "outputHashing""none",  
  32.     "deployUrl""Your Angular files location url"  
  33.   },  
  34.   "qaEmployee": {  
  35.     "optimization"true,  
  36.     "outputHashing""bundles",  
  37.     "sourceMap"false,  
  38.     "extractCss"true,  
  39.     "namedChunks"false,  
  40.     "aot"true,  
  41.     "extractLicenses"true,  
  42.     "vendorChunk"false,  
  43.     "buildOptimizer"true,  
  44.     "deployUrl""Your Angular files location url"  
  45.   },  
  46.   "prodEmployee": {  
  47.     "optimization"true,  
  48.     "outputHashing""bundles",  
  49.     "sourceMap"false,  
  50.     "extractCss"true,  
  51.     "namedChunks"false,  
  52.     "aot"true,  
  53.     "extractLicenses"true,  
  54.     "vendorChunk"false,  
  55.     "buildOptimizer"true,  
  56.     "deployUrl""Your Angular files location url"  
  57.   }  
  58. }  
Step 2 
 
Now run the below code for a different environment:
 
ng build --configuration=devEmployee -- For Dev Environment (I have run this command for our project.)
ng build --configuration=qaEmployee -- For QA Environment
ng build --configuration=prodEmployee -- For Production Environment
 
Step 3
 
Once the build is completed, rename the following highlighted files by navigating to project directory/dist/projectname (Your files may differ from mine depending upon the angular version, for Angular 9, these files are generated after the build)
 
For devEmployee build
polyfills-es5.js to polyfills.js 
vendor-es5.js to vendor.js
runtime-es5.js to runtime.js
styles-es5.js to styles.js
main-es5.js to main.js 
 
 
For qaEmployee build
 
 
For prodEmployee build /
 
 
Step 4
 
After renaming the highlighted files, upload all the files from projectdirectory/dist/projectname (D:\Arvind\solution\SharepointWithAngular\dist\SharepointWithAngular) to Sharepoint library created in previous article (SiteAssets/AngularDocs/employee/).
 
Step 5
 
Create one page(employee.aspx) in Sharepoint and add contenteditor in it.
 
 
 
Step 6
 
Create one employee.html file, add all js and CSS file in it, then upload the file into SharePoint library(SiteAssets/AngularDocs/html/).
 
To use angular in SharePoint, add <app-root></app-root> in your HTML (employee.html) 
  1. <!-- <link rel="stylesheet" href="/sites/Your Site Name/SiteAssets/AngularDocs/employee/styles.css?ver=1.0"> -->  
  2. <!-- <base href="/sites/Your Site Name/employee"> -->
  3. <meta name="viewport" content="width=device-width, initial-scale=1">  
  4. <app-root></app-root>  
  5. <script type="text/javascript">  
  6.     // call when dom loaded  
  7.     document.addEventListener('DOMContentLoaded', function () {  
  8.         executeDelayLoad();  
  9.         setInterval(reloadContextInfo, 1200000);  
  10.     }, false);  
  11.     // store all the angular js file into array.  
  12.     function executeDelayLoad() {  
  13.         var array = [  
  14.             "/sites/Your Site Name/SiteAssets/AngularDocs/employee/runtime.js?ver=1.0",  
  15.             "/sites/Your Site Name/SiteAssets/AngularDocs/employee/polyfills.js?ver=1.0",  
  16.             "/sites/Your Site Name/SiteAssets/AngularDocs/employee/vendor.js?ver=1.0",  
  17.             "/sites/Your Site Name/SiteAssets/AngularDocs/employee/main.js?ver=1.0",  
  18.             "/sites/Your Site Name/SiteAssets/AngularDocs/employee/styles.js?ver=1.0"  
  19.         ]  
  20.         loadJSFile(array);  
  21.     }  
  22.     // create script tag and add the angular js file to page.  
  23.     function loadJSFile(arrayJS) {  
  24.         var script = arrayJS.shift();  
  25.         var jsElm = document.createElement("script");  
  26.         // set the type attribute  
  27.         jsElm.type = "text/javascript";  
  28.         // make the script element load file  
  29.         jsElm.src = script;  
  30.         jsElm.async = false;  
  31.         jsElm.onload = function (script) {  
  32.             //console.log(script + ' loaded!');  
  33.             if (arrayJS.length) {  
  34.                 loadJSFile(arrayJS);  
  35.             } else {  
  36.                 window.employeeComponentReference.zone.run(() => { window.employeeComponentReference.loadEmployeeDashboard(); });  
  37.             }  
  38.   
  39.         };  
  40.         // finally insert the element to the body element in order to load the script  
  41.         document.getElementById("contentBox").parentElement.parentElement.appendChild(jsElm);  
  42.   
  43.     }  
  44.     // IF session expire reload the context of page.  
  45.     function reloadContextInfo() {  
  46.         $.ajax({  
  47.             url: _spPageContextInfo.webAbsoluteUrl + "/_api/contextinfo",  
  48.             method: "POST",  
  49.             headers: { "Accept""application/json; odata=verbose" },  
  50.             success: function (data) {  
  51.                 $('#__REQUESTDIGEST').val(data.d.GetContextWebInformation.FormDigestValue)  
  52.             },  
  53.             error: function (data, errorCode, errorMessage) {  
  54.                 alert("Session timed out. Page will be refreshed.");  
  55.                 window.location.reload();  
  56.             }  
  57.         });  
  58.     }  
  59. </script>  
Step 7
 
Finally, refer this html(employee.html) file in (Employee.aspx) file content editor.
 
Open Employee.aspx file in edit mode and edit the content editor to add employee.html in the Context link section.
 
 
Once done check-in and publish the Employee.aspx page, So that it be available for everyone who has permission.
 
Now browse employee.aspx page to perform the crud operation using angular and SharePoint online. 
 
To Add Employee
 
 Click on Add New Employee button and enter the values in fields. 
 
 
 To Edit
 
Click on the three dots of the last column in the table to perform the appropriate action (Edit) operation.
 
 
 
 
 To View
 
 To view the data in the right side layout, click the three dots, and select the View option.
 
 
 
To delete 
 
To delete the employee, click on three dots and select the Delete option 
 
 
 
Additionally, pagination, sorting, and ordering are also available in the demo.
 

Conclusion

 
Now we know how to create a SharePoint project with the latest angular without using the SharePoint framework.
 
Hope you have enjoyed it. Thank You  
 
You can download the complete angular project from the attachment.