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. @Injectable({
  3. providedIn: 'root'
  4. })
  5. export class ConstantService {
  6. constructor() { }
  7. // Define constant for list names
  8. public listNames = {
  9. Employee: {
  10. name: 'Employee',
  11. type: 'SP.Data.EmployeeListItem'
  12. }
  13. }
  14. // Define REST API Query in Constant file.
  15. public QUERY = {
  16. GET_CHOICEFIELD: {
  17. filter: 'EntityPropertyName eq \'{{choiceField}}\''
  18. },
  19. GET_EMPLOYEE: {
  20. select: 'ID,Title,FirstName,LastName,Address,Role,IsActive,PhoneNo,Created,Modified,Editor/ID,Editor/Title',
  21. expand: 'Editor',
  22. filter: 'IsActive eq \'{{isActive}}\''
  23. }
  24. }
  25. }
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. @Injectable({
  4. providedIn: 'root'
  5. })
  6. export class CommonService {
  7. constructor(
  8. private datePipe: DatePipe
  9. ) { }
  10. /**
  11. * construct a request to filter the array data into unique records.
  12. *
  13. * @description
  14. *
  15. * It will filter the array with unique records and return the filter array.
  16. *
  17. * @param array The array parameters contains an array which is required to filter the data.
  18. *
  19. * @return array It will returns an array having a unique value into the array.
  20. */
  21. uniqueArrayObj(array: any) {
  22. let sts: any = '';
  23. return sts = Array.from(new Set(array.map(s => s.label))).map(label1 => {
  24. return {
  25. label: label1,
  26. value: array.find(s => s.label === label1).value
  27. };
  28. });
  29. }
  30. // Sort array with property as label & value
  31. sortData(array: any) {
  32. return array.sort((a, b) => {
  33. if (a.label && a.label !== null && b.label && b.label !== null) {
  34. if (a.label.toLowerCase() < b.label.toLowerCase()) {
  35. return -1;
  36. }
  37. if (a.label.toLowerCase() > b.label.toLowerCase()) {
  38. return 1;
  39. }
  40. }
  41. return 0;
  42. })
  43. }
  44. /**
  45. * This method is used to sort number.
  46. * @param array Pass the array as parameter.
  47. */
  48. sortNumberArray(array: any) {
  49. return array.sort((a, b) => {
  50. if (parseFloat(a.label) && parseFloat(b.label)) {
  51. return a.label - b.label;
  52. }
  53. return 0;
  54. })
  55. }
  56. /**
  57. * This method is used to sort the DateArray;
  58. * @param array Pass the array as parameter.
  59. */
  60. sortDateArray(array: any) {
  61. const reverseDateRepresentation = date => {
  62. if (date) {
  63. let d1 = this.datePipe.transform(date.label ? date.label : date, 'dd-MM-yyyy');
  64. let parts = d1.split('-');
  65. let sortedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;
  66. return sortedDate;
  67. } else {
  68. return null;
  69. }
  70. };
  71. const sortedDates = array
  72. .map(reverseDateRepresentation)
  73. .sort()
  74. .map(reverseDateRepresentation);
  75. return sortedDates;
  76. }
  77. }
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. * This method is used to read the ODATA properties of sharepoint url and bind them.
  60. * @param url pass the url as parameters
  61. * @param options pass the options
  62. */
  63. readBuilder(url: string, options: any): string {
  64. if (options) {
  65. if (options.select) {
  66. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$select=' + options.select;
  67. }
  68. if (options.filter) {
  69. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$filter=' + options.filter;
  70. }
  71. if (options.expand) {
  72. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$expand=' + options.expand;
  73. }
  74. if (options.orderby) {
  75. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$orderby=' + options.orderby;
  76. }
  77. if (options.top) {
  78. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$top=' + options.top;
  79. }
  80. if (options.skip) {
  81. url += ((url.indexOf('?') === -1) ? '?' : '&') + '$skip=' + options.skip;
  82. }
  83. }
  84. return url;
  85. }
  86. /**
  87. * This method is used to get the Item url for sharepoint
  88. * @param listName pass the listname as parameter
  89. * @param id pass the id for update and delete operation
  90. * @param options pass data in options parameter if you want to update else donot pass.
  91. */
  92. getItemURL(listName: string, id: any, options?: any) {
  93. let url = this.apiUrl.replace('{0}', listName) + '(' + id + ')';
  94. url = this.readBuilder(url, options);
  95. return url;
  96. }
  97. /**
  98. * This method is used to build the get items url
  99. * @param listName Pass the listname as parameter
  100. * @param options Pass the options as parameter.
  101. */
  102. getReadURL(listName: string, options?: any) {
  103. let url = this.apiUrl.replace('{0}', listName);
  104. url = this.readBuilder(url, options);
  105. return url;
  106. }
  107. /**
  108. * This method will create the new item in sharepoint list
  109. * @param listName pass the listname
  110. * @param jsonBody pass the body
  111. * @param type pass the type
  112. */
  113. async createItem(listName: string, jsonBody: any, type: string) {
  114. const url = this.getReadURL(listName, null);
  115. // append metadata
  116. if (!jsonBody.__metadata) {
  117. jsonBody.__metadata = {
  118. // tslint:disable-next-line:object-literal-key-quotes
  119. 'type': type
  120. };
  121. }
  122. // Call the http post method
  123. const data = JSON.stringify(jsonBody);
  124. const res = await this.httpClient.post(url, data, this.getHeaders(true, true)).toPromise().catch((err: HttpErrorResponse) => {
  125. const error = err.error;
  126. return error;
  127. });
  128. return res.d.results;
  129. }
  130. /**
  131. * This method is used to update the item in sharepoint list
  132. * @param listName pass the list name as parameter
  133. * @param id pass the id of the item.
  134. * @param jsonBody pass the data which you want to update
  135. * @param type pass the type of list.
  136. */
  137. async updateItem(listName: string, id: any, jsonBody: any, type?: string) {
  138. // Append HTTP header MERGE for UPDATE scenario
  139. const localOptions = this.getHeaders(true, false);
  140. localOptions['X-HTTP-Method'] = 'MERGE';
  141. localOptions['If-Match'] = '*';
  142. // Append metadata
  143. if (!jsonBody.__metadata) {
  144. jsonBody.__metadata = {
  145. // tslint:disable-next-line:object-literal-key-quotes
  146. 'type': type
  147. };
  148. }
  149. // stringify the data
  150. const data = JSON.stringify(jsonBody);
  151. const url = this.getItemURL(listName, id);
  152. const httpOptions = {
  153. headers: new HttpHeaders(localOptions)
  154. };
  155. // Call the http post method
  156. await this.httpClient.post(url, data, httpOptions).toPromise().catch((err: HttpErrorResponse) => {
  157. const error = err.error;
  158. return error;
  159. });
  160. }
  161. /**
  162. * This method is used to read items from sharepoint list based on REST query.
  163. * @param listName Pass the listname as a parameter
  164. * @param options pass the query
  165. */
  166. async readItems(listName: string, options?: any) {
  167. // build the read url
  168. const url = this.getReadURL(listName, options);
  169. let res;
  170. // Call the http get method
  171. res = await this.httpClient.get(url, this.getHeaders(true, true)).toPromise().catch((err: HttpErrorResponse) => {
  172. const error = err.error;
  173. return error;
  174. });
  175. return res.d.results;
  176. }
  177. /**
  178. * This method is used to delete items from sharepoint list based on REST query.
  179. * @param listName Pass the listName as a parameter.
  180. * @param id pass the list item Id
  181. */
  182. async deleteItem(listName: string, id: number) {
  183. // Append HTTP header Delete for Delete scenario
  184. const localOptions = this.getHeaders(true, false);
  185. localOptions['X-HTTP-Method'] = 'DELETE'; // set the delete header for post call.
  186. localOptions['If-Match'] = '*';
  187. const url = this.getItemURL(listName, id);
  188. const httpOptions = {
  189. headers: new HttpHeaders(localOptions)
  190. };
  191. // build the delete url
  192. let res;
  193. // Call the http delete method
  194. res = await this.httpClient.post(url, null, httpOptions).toPromise().catch((err: HttpErrorResponse) => {
  195. const error = err.error;
  196. return error;
  197. });
  198. return res;
  199. }
  200. /**
  201. * This method is used to get the choice field value from list
  202. * @param listName Pass the list name as parameter
  203. * @param options Pass the optiosn as parameter
  204. */
  205. async getChoiceFieldItems(listName: string, options?: any) {
  206. const url = this.getChoiceFieldUrl(listName, options);
  207. let res;
  208. res = await this.httpClient.get(url, this.getHeaders(true, true)).toPromise().catch((err: HttpErrorResponse) => {
  209. const error = err.error;
  210. return error;
  211. });
  212. return res.d.results;
  213. }
  214. /**
  215. * This method is used to build the url for choice columns.
  216. * @param listName Pass the list name as parameter
  217. * @param options Pass the options as parameter.
  218. */
  219. getChoiceFieldUrl(listName: string, options?: any) {
  220. const choiceUrl = this.baseUrl + '/_api/web/lists/GetByTitle(\'{listName}\')/fields';
  221. let url = choiceUrl.replace('{listName}', listName);
  222. url = this.readBuilder(url, options);
  223. return url;
  224. }
  225. }
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. @Injectable({
  3. providedIn: 'root'
  4. })
  5. export class GlobalObjectService {
  6. constructor() { }
  7. //Define the SharePoint Object.
  8. public sharePointPageObject = {
  9. webAbsoluteUrl: '',
  10. webRelativeUrl: '',
  11. userId: 0
  12. };
  13. }
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. @Component({
  6. selector: 'app-root',
  7. templateUrl: './app.component.html',
  8. styleUrls: ['./app.component.css'],
  9. encapsulation: ViewEncapsulation.None
  10. })
  11. export class AppComponent {
  12. constructor(
  13. private globalService: GlobalObjectService,
  14. private router: Router,
  15. private _ngZone: NgZone
  16. ) { }
  17. /**
  18. * This method will initialize the required variable for project
  19. */
  20. ngOnInit() {
  21. // Set the webAbsoluteUrl for localhost and sharepoint page
  22. this.globalService.sharePointPageObject.webAbsoluteUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'
  23. : _spPageContextInfo.webAbsoluteUrl;
  24. this.globalService.sharePointPageObject.webRelativeUrl = window.location.href.indexOf('localhost') > -1 ? '/sites/Your Site Name'
  25. : _spPageContextInfo.webRelativeUrl;
  26. this.globalService.sharePointPageObject.userId = window.location.href.indexOf('localhost') > -1 ? 'Your User Id': _spPageContextInfo.userId;
  27. // This will call first when employee page loads
  28. window['employeeComponentReference'] = {
  29. component: this, zone: this._ngZone,
  30. loadEmployeeDashboard: () => this.goToEmployeeDashboard(),
  31. };
  32. }
  33. // This method set the routing first time.
  34. goToEmployeeDashboard() {
  35. if (window.location.href.includes('employee') || !window.location.href.includes('#')) {
  36. this.router.navigate(['/employee']);
  37. }
  38. }
  39. }
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-family: Helvetica;
  4. font-size: 14px;
  5. }
  6. .empTable div.ui-table-wrapper {
  7. overflow: visible!important;
  8. }
  9. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child {
  10. overflow: visible;
  11. }
  12. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div {
  13. position: relative;
  14. }
  15. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div>i {
  16. top: 0px !important;
  17. }
  18. .empTable .ui-table-wrapper tbody.ui-table-tbody>tr>td:last-child>div div.ui-menu {
  19. top: 0px !important;
  20. left: inherit !important;
  21. right: 40px !important;
  22. }
  23. .minFirstLastCol5Per .ui-table .ui-table-thead > tr> th:first-child,
  24. .minFirstLastCol5Per .ui-table .ui-table-thead > tr> th:last-child,
  25. .minFirstLastCol5Per .ui-table .ui-table-tbody > tr> td:first-child,
  26. .minFirstLastCol5Per .ui-table .ui-table-tbody > tr> td:last-child {
  27. width: 5% !important;
  28. }
  29. .minFirstLastCol5Per .ui-table .ui-table-tbody tr> td{
  30. word-break: break-all;
  31. }
  32. .clearBoth {
  33. clear: both;
  34. }
  35. .compulsory {
  36. display: inline;
  37. padding-right: 2px;
  38. color: #c53e3e;
  39. }
  40. .mr-5 {
  41. margin-right: 5px;
  42. }
  43. .mt-20 {
  44. margin-top: 20px;
  45. }
  46. input,
  47. optgroup,
  48. select,
  49. textarea {
  50. padding: 6px 20px 6px 6px !important;
  51. }
  52. .error {
  53. color: red !important;
  54. }
  55. .spanner {
  56. position: absolute;
  57. /* top: calc( 100vh - 35vh); */
  58. padding: 50% 0% 35% 0%;
  59. left: 0;
  60. background: #2a2a2a55;
  61. width: 100%;
  62. display: block;
  63. text-align: center;
  64. color: #FFF;
  65. transform: translateY(-50%);
  66. z-index: 1000;
  67. visibility: hidden;
  68. }
  69. .overlay {
  70. position: fixed;
  71. width: 100%;
  72. height: 100%;
  73. visibility: hidden;
  74. }
  75. .spanner,
  76. .overlay {
  77. opacity: 0;
  78. -webkit-transition: all 0.3s;
  79. -moz-transition: all 0.3s;
  80. transition: all 0.3s;
  81. }
  82. .spanner.show,
  83. .overlay.show {
  84. opacity: 1
  85. }
  86. .show {
  87. visibility: visible;
  88. }
  89. .loader,
  90. .loader:before,
  91. .loader:after {
  92. border-radius: 50%;
  93. width: 2.5em;
  94. height: 2.5em;
  95. -webkit-animation-fill-mode: both;
  96. animation-fill-mode: both;
  97. -webkit-animation: load7 1.8s infinite ease-in-out;
  98. animation: load7 1.8s infinite ease-in-out;
  99. }
  100. .loader {
  101. color: #ffffff;
  102. font-size: 10px;
  103. margin: 30px auto;
  104. position: relative;
  105. text-indent: -9999em;
  106. -webkit-transform: translateZ(0);
  107. -ms-transform: translateZ(0);
  108. transform: translateZ(0);
  109. -webkit-animation-delay: -0.16s;
  110. animation-delay: -0.16s;
  111. margin-top: 20%;
  112. }
  113. .loader:before,
  114. .loader:after {
  115. content: '';
  116. position: absolute;
  117. top: 0;
  118. }
  119. .loader:before {
  120. left: -3.5em;
  121. -webkit-animation-delay: -0.32s;
  122. animation-delay: -0.32s;
  123. }
  124. .loader:after {
  125. left: 3.5em;
  126. }
  127. @-webkit-keyframes load7 {
  128. 0%,
  129. 80%,
  130. 100% {
  131. box-shadow: 0 2.5em 0 -1.3em;
  132. }
  133. 40% {
  134. box-shadow: 0 2.5em 0 0;
  135. }
  136. }
  137. @keyframes load7 {
  138. 0%,
  139. 80%,
  140. 100% {
  141. box-shadow: 0 2.5em 0 -1.3em;
  142. }
  143. 40% {
  144. box-shadow: 0 2.5em 0 0;
  145. }
  146. }
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. // Declare the route with following code
  5. const routes: Routes = [
  6. { path: '', redirectTo: 'employee', pathMatch: 'full' },
  7. { path: 'employee', component: EmployeeComponent, pathMatch: 'full' }
  8. ];
  9. @NgModule({
  10. imports: [RouterModule.forRoot(routes, { useHash: true, onSameUrlNavigation: 'reload' })],
  11. exports: [RouterModule]
  12. })
  13. // export the AppRouting module.
  14. 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. @ViewChild("loader", { static: false }) loaderView: ElementRef;
  50. @ViewChild("spanner", { static: false }) spannerView: ElementRef;
  51. /**
  52. * The constructor will create the object of following classes.
  53. * @param frmbuilder
  54. * @param constant
  55. * @param spServices
  56. */
  57. constructor(
  58. private frmbuilder: FormBuilder,
  59. private constant: ConstantService,
  60. private spServices: SPOperationService,
  61. private messageService: MessageService,
  62. private datepipe: DatePipe,
  63. private common: CommonService,
  64. private confirmationService: ConfirmationService
  65. ) { }
  66. ngOnInit(): void {
  67. this.showTable = false;
  68. this.isEmployeeFormVisible = false;
  69. // Initialize the employee form.
  70. this.initEmployeeForm();
  71. // put the value into employee column array.
  72. this.employeeColumns = [
  73. { field: 'FirstName', header: 'First Name', visibility: true },
  74. { field: 'LastName', header: 'Last Name', visibility: true },
  75. { field: 'FullName', header: 'Full Name', visibility: true },
  76. { field: 'PhoneNo', header: 'PhoneNo Number', visibility: true },
  77. { field: 'Address', header: 'Address', visibility: true },
  78. { field: 'Role', header: 'Role', visibility: true },
  79. { field: 'IsActive', header: 'IsActive', visibility: true },
  80. { field: 'Modified', header: 'Last Updated Date', visibility: true },
  81. { field: 'ModifiedBy', header: 'Last Updated By', visibility: true },
  82. ];
  83. this.loadEmployeeTable();
  84. }
  85. /**
  86. * This method is used to get the value from employee list.
  87. * and pass that store that value into employeeDataArray = [];
  88. */
  89. async loadEmployeeTable() {
  90. this.isEmployeeFormVisible = false;
  91. this.employeeDataArray = [];
  92. // Prepare the Get query
  93. const getEmployeeOptions = Object.assign({}, this.constant.QUERY.GET_EMPLOYEE);
  94. getEmployeeOptions.filter = getEmployeeOptions.filter.replace(/{{isActive}}/gi, 'Yes');
  95. // Call the SharePoint Rest GET
  96. const results = await this.spServices.readItems(this.constant.listNames.Employee.name, getEmployeeOptions);
  97. // Check the data in results
  98. if (results && results.length) {
  99. const tempArray = [];
  100. // loop through array and store into the employeeData Array.
  101. results.forEach(element => {
  102. const employeeObj: IEmployee = {
  103. ID: element.ID,
  104. FullName: element.Title,
  105. FirstName: element.FirstName,
  106. LastName: element.LastName,
  107. PhoneNo: element.PhoneNo,
  108. Role: element.Role,
  109. Address: element.Address,
  110. IsActive: element.IsActive,
  111. Created: element.Created,
  112. Modified: new Date(this.datepipe.transform(element.Modified, 'MMM dd, yyyy')),
  113. ModifiedID: element.Editor.ID,
  114. ModifiedBy: element.Editor.Title
  115. }
  116. tempArray.push(employeeObj);
  117. });
  118. this.employeeDataArray = tempArray;
  119. // Assign the value to multi-select
  120. this.colFilterData(tempArray);
  121. this.showTable = true;
  122. }
  123. }
  124. /**
  125. * This method is used to uniquely assign the value to multiselect dropdown
  126. * @param colData
  127. */
  128. colFilterData(colData) {
  129. // sort and unique data assign to multiselect firstNameArray
  130. this.employeeMultiColArray.firstNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  131. const b = {
  132. label: a.FirstName, value: a.FirstName
  133. };
  134. return b;
  135. })));
  136. // sort and unique data assign to multiselect lastNameArray
  137. this.employeeMultiColArray.lastNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  138. const b = {
  139. label: a.LastName, value: a.LastName
  140. };
  141. return b;
  142. })));
  143. // sort and unique data assign to multiselect fullNameArray
  144. this.employeeMultiColArray.fullNameArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  145. const b = {
  146. label: a.FullName, value: a.FullName
  147. };
  148. return b;
  149. })));
  150. // sort and unique data assign to multiselect phoneNoArray
  151. this.employeeMultiColArray.phoneNoArray = this.common.sortNumberArray(this.common.uniqueArrayObj(colData.map(a => {
  152. const b = {
  153. label: a.PhoneNo, value: a.PhoneNo
  154. };
  155. return b;
  156. })));
  157. // sort and unique data assign to multiselect roleArray
  158. this.employeeMultiColArray.roleArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  159. const b = {
  160. label: a.Role, value: a.Role
  161. };
  162. return b;
  163. })));
  164. // sort and unique data assign to multiselect addressArray
  165. this.employeeMultiColArray.addressArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  166. const b = {
  167. label: a.Address, value: a.Address
  168. };
  169. return b;
  170. })));
  171. // sort and unique data assign to multiselect isActiveArray
  172. this.employeeMultiColArray.isActiveArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  173. const b = {
  174. label: a.IsActive, value: a.IsActive
  175. };
  176. return b;
  177. })));
  178. // 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)));
  179. 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)));
  180. 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);
  181. // sort and unique data assign to multiselect lastModifiedArray
  182. this.employeeMultiColArray.lastModifiedByArray = this.common.sortData(this.common.uniqueArrayObj(colData.map(a => {
  183. const b = {
  184. label: a.ModifiedBy, value: a.ModifiedBy
  185. };
  186. return b;
  187. })));
  188. }
  189. optionFilter(event: any) {
  190. if (event.target.value) {
  191. this.isOptionFilter = false;
  192. }
  193. }
  194. storeRowData(rowData) {
  195. this.currEmployeeObj = rowData;
  196. // Add menu to pMenuItems Array
  197. this.pMenuItems = [
  198. { label: 'Edit', command: (e) => this.editEmployee(rowData) },
  199. { label: 'View', command: (e) => this.viewEmployee(rowData) },
  200. { label: 'Mark As InActive', command: (e) => this.markAsInactive() },
  201. { label: 'Delete', command: (e) => this.deleteEmployee() }
  202. ];
  203. console.log(rowData);
  204. }
  205. /**
  206. * This method is used to show the Add employee form.
  207. */
  208. showAddEditEmployeeForm() {
  209. // make isAddEmployee variable to true. So that add employee form will visible.
  210. this.loaderView.nativeElement.classList.add('show');
  211. this.spannerView.nativeElement.classList.add('show');
  212. this.employeeForm.reset();
  213. this.loadDropDown();
  214. setTimeout(() => {
  215. this.isAddEmployee = true;
  216. }, 500);
  217. this.isEmployeeFormVisible = true;
  218. this.spannerView.nativeElement.classList.remove('show');
  219. }
  220. async loadDropDown() {
  221. this.isActiveDropArray = [];
  222. this.roleDropArray = [];
  223. // Get the choice column role value form Employee list (Role is choice column);
  224. const getRoleInfo = Object.assign({}, this.constant.QUERY.GET_CHOICEFIELD);
  225. getRoleInfo.filter = getRoleInfo.filter.replace(/{{choiceField}}/gi, 'Role');
  226. // Get choice value
  227. const choiceResults = await this.spServices.getChoiceFieldItems(this.constant.listNames.Employee.name, getRoleInfo);
  228. console.log("Role Array value", choiceResults);
  229. if (choiceResults && choiceResults.length) {
  230. const tempArray = choiceResults[0].Choices.results;
  231. this.roleDropArray = [];
  232. // loop through each choice and add into roleDropdown array.
  233. tempArray.forEach(element => {
  234. this.roleDropArray.push({ label: element, value: element });
  235. });
  236. }
  237. // Here you can again make call to employee list to get the choice column value
  238. // But I have used the static value here.
  239. this.isActiveDropArray.push(
  240. { label: 'Yes', value: 'Yes' },
  241. { label: 'No', value: 'No' }
  242. );
  243. }
  244. /**
  245. * This method is used to edit the employee
  246. * @param currObj
  247. */
  248. async editEmployee(currObj) {
  249. this.spannerView.nativeElement.classList.add('show');
  250. await this.loadDropDown();
  251. this.employeeForm.get('firstName').setValue(currObj.FirstName);
  252. this.employeeForm.get('lastName').setValue(currObj.LastName);
  253. this.employeeForm.get('role').setValue(currObj.Role);
  254. this.employeeForm.get('address').setValue(currObj.Address);
  255. this.employeeForm.get('phoneNum').setValue(currObj.PhoneNo);
  256. this.employeeForm.get('isActive').setValue(currObj.IsActive);
  257. this.isAddEmployee = false;
  258. this.isEmployeeFormVisible = true;
  259. this.spannerView.nativeElement.classList.remove('show');
  260. }
  261. /**
  262. * This method is used to show the employee on right side.
  263. * @param currObj
  264. */
  265. viewEmployee(currObj) {
  266. this.isEmployeeSideVisible = true;
  267. this.spannerView.nativeElement.classList.add('show');
  268. this.employeeRightSideArray = [currObj]
  269. this.spannerView.nativeElement.classList.remove('show');
  270. }
  271. /**
  272. * This method is used to mark the current employee as Inactive
  273. * @param currObj Pass the current employee obj
  274. */
  275. async markAsInactive() {
  276. // Pop messages.
  277. this.confirmationService.confirm({
  278. message: 'Do you want to mark this record as Inactive?',
  279. header: 'Delete Confirmation',
  280. icon: 'pi pi-info-circle',
  281. key: 'confirm',
  282. accept: () => {
  283. this.onSubmit('InActive');
  284. },
  285. });
  286. }
  287. /**
  288. * This method is used to perform action based on operation parameter
  289. *
  290. * If InActive - It will update the Inactive='No' in Employee List.
  291. * If delete - It will delete the record from list.
  292. * @param operation
  293. */
  294. async onSubmit(operation) {
  295. this.spannerView.nativeElement.classList.add('show');
  296. if (operation === 'InActive') {
  297. // prepare the Json body
  298. const data = {
  299. __metadata: { type: this.constant.listNames.Employee.type },
  300. IsActive: 'No'
  301. };
  302. // call the update function
  303. await this.spServices.updateItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID, data, this.constant.listNames.Employee.type);
  304. this.messageService.add({
  305. key: 'employeeToast', severity: 'success', summary: 'Success message',
  306. detail: 'This employee marked as InActive in the list.'
  307. });
  308. await this.loadEmployeeTable();
  309. this.spannerView.nativeElement.classList.remove('show');
  310. }
  311. if (operation === 'delete') {
  312. this.spannerView.nativeElement.classList.add('show');
  313. // it will delete the employee record from list.
  314. await this.spServices.deleteItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID);
  315. this.messageService.add({
  316. key: 'employeeToast', severity: 'success', summary: 'Success message',
  317. detail: 'This employee deleted from list.'
  318. });
  319. await this.loadEmployeeTable();
  320. this.spannerView.nativeElement.classList.remove('show');
  321. }
  322. }
  323. /**
  324. * This method get called when delete option is selected form menu.
  325. * @param currObj pass the current employee object.
  326. */
  327. async deleteEmployee() {
  328. this.confirmationService.confirm({
  329. message: 'Do you want to delete this record?',
  330. header: 'Delete Confirmation',
  331. icon: 'pi pi-info-circle',
  332. key: 'confirm',
  333. accept: () => {
  334. this.onSubmit('delete');
  335. },
  336. });
  337. }
  338. /**
  339. * This method is used to add/update the employee information into sharepoint list.
  340. * If variable contains the value as per following
  341. *
  342. * isAddEmployee = true - It will add item to list.
  343. * isAddEmployee = false - It will update item to list.
  344. *
  345. */
  346. async saveEmployee() {
  347. // hide the spanner.
  348. // this.spannerView.nativeElement.classList.remove('show');
  349. // Allow the form to be able to submit it.
  350. this.loaderView.nativeElement.classList.add('show');
  351. this.isEmployeeFormSubmit = true;
  352. //check if isAddEmployee is true or not
  353. if (this.isAddEmployee) { // add employee
  354. if (this.employeeForm.valid) {
  355. this.spannerView.nativeElement.classList.add('show');
  356. const empData = this.getEmployeeData();
  357. const result = this.spServices.createItem(this.constant.listNames.Employee.name, empData, this.constant.listNames.Employee.type);
  358. this.messageService.add({
  359. key: 'employeeToast', severity: 'success', summary: 'Success message',
  360. detail: 'Employee added into the list.'
  361. });
  362. this.isAddEmployee = false;
  363. await this.loadEmployeeTable();
  364. //this.loaderView.nativeElement.classList.remove('show');
  365. this.spannerView.nativeElement.classList.remove('show');
  366. }
  367. } else { // update employee
  368. this.spannerView.nativeElement.classList.add('show');
  369. const data = this.getEmployeeData();
  370. await this.spServices.updateItem(this.constant.listNames.Employee.name, this.currEmployeeObj.ID, data, this.constant.listNames.Employee.type);
  371. this.messageService.add({
  372. key: 'employeeToast', severity: 'success', summary: 'Success message',
  373. detail: 'Employee updated Successfully.'
  374. });
  375. this.isAddEmployee = false;
  376. await this.loadEmployeeTable();
  377. this.spannerView.nativeElement.classList.remove('show');
  378. }
  379. }
  380. cancelEmployee() {
  381. this.isEmployeeFormVisible = false;
  382. //reset the form value.
  383. this.employeeForm.reset();
  384. }
  385. /**
  386. * This method is used to get the form value and prepare the body data for creating new item in employee list
  387. *
  388. */
  389. getEmployeeData() {
  390. // Assingn the form value to local variable
  391. const employeeForm = this.employeeForm.value;
  392. // prepare the body data for post call
  393. const data: any = {
  394. __metadata: { type: this.constant.listNames.Employee.type },
  395. Title: employeeForm.firstName + ' ' + employeeForm.lastName,
  396. FirstName: employeeForm.firstName,
  397. LastName: employeeForm.lastName,
  398. PhoneNo: employeeForm.phoneNum,
  399. Address: employeeForm.address,
  400. Role: employeeForm.role,
  401. IsActive: employeeForm.isActive
  402. };
  403. // return the data.
  404. return data;
  405. }
  406. /**
  407. * This method is used to initialize the employee form.
  408. */
  409. initEmployeeForm() {
  410. this.employeeForm = this.frmbuilder.group({
  411. firstName: ['', [Validators.required]],
  412. lastName: ['', [Validators.required]],
  413. phoneNum: ['', Validators.required],
  414. address: ['', Validators.required],
  415. role: ['', Validators.required],
  416. isActive: ['', Validators.required]
  417. });
  418. }
  419. }
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. // finally insert the element to the body element in order to load the script
  40. document.getElementById("contentBox").parentElement.parentElement.appendChild(jsElm);
  41. }
  42. // IF session expire reload the context of page.
  43. function reloadContextInfo() {
  44. $.ajax({
  45. url: _spPageContextInfo.webAbsoluteUrl + "/_api/contextinfo",
  46. method: "POST",
  47. headers: { "Accept": "application/json; odata=verbose" },
  48. success: function (data) {
  49. $('#__REQUESTDIGEST').val(data.d.GetContextWebInformation.FormDigestValue)
  50. },
  51. error: function (data, errorCode, errorMessage) {
  52. alert("Session timed out. Page will be refreshed.");
  53. window.location.reload();
  54. }
  55. });
  56. }
  57. </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.