Ashok Raja's Blog
SharePoint list data CRUD operation with PnP Core Js and Angular2 - SP Ng2 SPA Part 2
by Ashok Raja T 11. December 2016 20:54

This is the second article of the series on Angular 2 SharePoint Single Page App with Webpack. In this article we can see how to perform CRUD operation in a SharePoint Single Page App with Angular2 and SP PnP Js Core.

All article is this series

1. Environment setup and solution structure for Angular 2 Single Page App for SharePoint Online

2. SharePoint list data CRUD operation with PnP Core Js and Angular2 - SP Ng2 SPA Part 2 (This atricle)

3. Angular2 routing inside SharePoint SitePages Document Library - SP Ng2 SPA Part 3

4. Configuring webpack for development and production build in SharePoint On-line Angular2 SPA - SP Ng2 SPA Part 4

5. Practical difficulties and lessons learnt on Single Page App for SharePoint Online with Angular2 and Typescript - SP Ng2 SPA Part 5

Add a reference to “sp-pnp-js” ( added via package.json)  in vendor.browser.ts so that pnp js library would be included as part of the build. Reference to “rxjs” can be removed from this project as “SP PnP Core Js” relies on regular promise based http requests at this time of writing. Removing this file would reduce the file size of “vendor.[hash].js” in production build.

image

Data Source

The next step is to prepare the data source. Create a list named “Employee” with fields “Title”, “Location”,”Designation”,”Email”. Once the list is created, add an entity object to the project so that it can be used as the medium for data communication. The below is the structure of entity object.


 export interface IEmployeeEntity {
     Id: number;
     Title: string;
     Location: string;
     Designation: string;
     Email: string;
 }

Manually creating this object ( interface) may be painful and time consuming. But there are easy options to automatically create this file. Add a record to the list and access it via REST API. Copy the generated JSON object and convert it into Typescript objects from http://json2ts.com/

Interface

The next step is to create an interface named “IEmployee” that would be implemented by other components that needs to interact with the “Employee” object. In this project , “EmployeeAddComponent” and “EmployeeEditComponent” implements this interface.


 import { Component, OnInit } from '@angular/core';
 import { IEmployeeEntity } from '../shared/app.entities';
 
 export interface IEmployee extends OnInit {
     loading: string;
     Employee: IEmployeeEntity;
     readonly pageTile: string;
     readonly pageMode: string;
 
     ngOnInit(): void
     saveChanges(): void;
 }

CRUD Operation with SP PnP Js Core

Add New Item (Extract from employee.add.component.ts)


     saveChanges() {
         this.loading = "init";
         new sp.Web(AppSettings.SHAREPOINT_SITE_URL).lists.getByTitle("Employee").items.add({
             Title: this.Employee.Title,
             Location: this.Employee.Location,
             Designation: this.Employee.Designation,
             Email: this.Employee.Email
         }).then((result) => {
             this.newEmployee = this.Employee.Title;
             this.itemAdded = true;
             setTimeout(function () {
                 this.itemAdded = false;
             }.bind(this), 3000);
             this.reset();
             this.loading = "done";
         }).catch((e) => { this.loading = "error"; });
     }

View List Data ( All Items) (Extract from home.component.ts)


    ngOnInit() {
         new pnp.Web(AppSettings.SHAREPOINT_SITE_URL).lists.getByTitle('Employee').items.get().then((result: any) => {
             this.Employees = result;
             this.loading = "done";
         }).catch((e) => { this.loading = "error"; });
     }

View , Edit, Delete Single Item


 import { Component, OnInit, Input } from '@angular/core';
 import { Router, ActivatedRoute } from '@angular/router';
 
 import { AppSettings } from '../../shared/app.settings';
 import { IEmployee } from '../../shared/app.interfaces';
 import { IEmployeeEntity } from '../../shared/app.entities';
 import { AppLoadingComponent } from '../../shared/components/loading/app.loading';
 
 import * as sp from "sp-pnp-js";
 
 @Component({
     templateUrl: '../employee.component.html',
 
 })
 export class EmployeeEditComponent implements IEmployee {
     private Id: string;
     @Input()
     Employee: IEmployeeEntity = null;
     pageTile = "Edit / Delete Employee";
     pageMode = "edit";
     loading: string = 'init';
 
     constructor(
         private appSettings: AppSettings,
         private activeRoute: ActivatedRoute,
         private router: Router
     ) { }
 
     ngOnInit() {
         this.activeRoute.params.subscribe(params => {
             this.Id = params['id'];
             new sp.Web(AppSettings.SHAREPOINT_SITE_URL).lists.getByTitle("Employee").items.getById(+this.Id).get().then((result) => {
                 this.Employee = result;
                 console.log(this.Employee);
                 this.loading = "done";
             }).catch((e) => { this.loading = "error"; });;
         });
     }
 
     deleteRecord(event: Event) {
         event.preventDefault();
         if (confirm('Do you wish to delete this record ?')) {
             this.loading = "init";
             new sp.Web(AppSettings.SHAREPOINT_SITE_URL).lists.getByTitle("Employee").items.getById(+this.Id).delete().then(() => {
                 this.loading = "done";
                 this.router.navigateByUrl('/home');
             }).catch((e) => { this.loading = "error"; });
 
         } else {
             console.log('no');
         }
     }
 
     saveChanges() {
         this.loading = "init";
         new sp.Web(AppSettings.SHAREPOINT_SITE_URL).lists.getByTitle("Employee").items.getById(this.Employee.Id).update({
             Title: this.Employee.Title,
             Location: this.Employee.Location,
             Designation: this.Employee.Designation,
             Email: this.Employee.Email
         }).then((result) => {
             console.log('Record Updated');
             this.loading = "done";
             this.router.navigateByUrl('/home');
         }).catch((e) => { this.loading = "error"; });
     }
 }

Loading Spinner


 import { Component } from '@angular/core';
 
 @Component({
     selector: 'app-loading',
     template:`
     <div>
         <div class="app-loading">
             <img src="./static/squares.gif" width="60" alt="Loading..."><br> &nbsp;&nbsp; Loading Components...
         </div>
     </div>`
 })
 
 export class AppLoadingComponent { }

If you look at the above code, the variable named “loading” controls the behaviour of the spinner that is displayed during the back end http operations.

Notification

All failed http operations are notified to user via Office UI Fabric’s “Message Bar”. This is created  as a component which can be invoked from any page.


 import { Component, Input } from '@angular/core';
 
 @Component({
     selector: 'app-notify',
     template: `
         <div>
             <div class="ms-MessageBar ms-MessageBar--{{msgType}}">
                 <div class="ms-MessageBar-content">
                     <div class="ms-MessageBar-icon">
                         <i class="ms-Icon ms-Icon--{{msgIcon}}"></i>
                     </div>
                     <div class="ms-MessageBar-text">
                         {{msgText}}
                     </div>
                 </div>
             </div>
         </div>`
 })
 
 export class AppNotifyComponent {
     @Input() msgText: string = "Error Occurred. Please contact your Administrator !!!";
     @Input() msgType: string = "error";
     @Input() msgIcon: string = "ErrorBadge";
 }

What's Next

In the next article , routing, app modules and components would be covered

blog comments powered by Disqus