Friday, December 11, 2020

CRUD Operations Using SPFx (SharePoint Online)

Everyone is talking about SPFx. Let's see what is it and how we can create it.

According to microsoft-
The SharePoint Framework (SPFx) is a page and web part model that provides full support for client-side SharePoint development, easy integration with SharePoint data, and extending Microsoft Teams. With the SharePoint Framework, you can use modern web technologies and tools in your preferred development environment to build productive experiences and apps that are responsive and mobile-ready.

Key features of the SharePoint Framework include:

  • It runs in the context of the current user and connection in the browser. There are no iFrames for the customization (JavaScript is embedded directly to the page).
  • The controls are rendered in the normal page DOM.
  • The controls are responsive and accessible by nature.
  • It enables the developer to access the lifecycle in addition to renderloadserialize and de-serializeconfiguration changes, and more.
  • It's framework-agnostic. You can use any JavaScript framework that you like including, but not limited to, React, Handlebars, Knockout, Angular, and Vue.js.
  • The developer toolchain is based on popular open-source client development tools such as NPM, TypeScript, Yeoman, webpack, and gulp.
  • Performance is reliable.
  • End users can use SPFx client-side solutions that are approved by the tenant administrators (or their delegates) on all sites, including self-service team, group, or personal sites.
  • SPFx web parts can be added to both classic and modern pages.
  • SPFx solutions can be used to extend Microsoft Teams.

Now, let us see, how we can create SPFx webpart. Before creating SPFx webpart, there are some pre-requisites that need to fulfill. Please refer SPFx Prerequisites page.

Let's begin-

  1. Open command prompt and move to the folder where you need to create SPFx solution. I am creating SPFx solution in D drive.

  2. Now create a folder where SPFx solution files will be created using command "md CRUDOperationsSPFx". You make choose any name as per your wish. "md" is the DOS command to create directory/folder.
  3. Change the directory path using command "cd CRUDOperationsSPFx". "cd" is the DOS command to change the directory/folder.

  4. Execute the command "yo @microsoft/sharepoint". It runs Yeoman SharePoint Generator to create a new SPFx web part.

  5. Sometimes, it happens that when we had installed all the pre requisites, it doesn't gets installed globally. In that case, it fails to recognize the command. In such case, find out the path where all the installations took place. Copy the complete path and paste it before "yo". Press Enter

  6. Now, it will ask a series of questions ( name can be of your choice 😊 ) -
    1. What is your solution name? : CRUDOperationsBySPFxSolution


    2. Which baseline packages do you want to target for your component(s)? : SharePoint Online only (latest)

    3. Where do you want to place the files? : Use the current folder

    4. Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites? : N


    5. Will the components in the solution require permissions to access web APIs that are unique and not shared with other components in the tenant? : N


    6. Which type of client-side component to create? : WebPart


  7. Apart form it, now it will ask the next set of questions that are specific about your web part ( name/description can be of your choice 😊 ) -
    1. What is your Web part name? : CRUDOperationsBySPFxWebPart

    2. What is your Web part description? : Manage CRUD Operations Using SPFx

    3. Which framework would you like to use? : No JavaScript framework

  8. Once you press Enter, it will start creating the SPFx webpart. It may take from 2-3 minutes to 20-25 minutes.

  9. Once completed, a Congratulations! message will be displayed on screen.

  10. Finally, WebPart has been created successfully. Now you may view it on local workbench, how it is looking. But we are stopping here as we have to modify it as per our need. Hence we will open the code in Visual Studio Code Editor. For this we will use "code ." command.

  11. Press Enter. It will open the SPFx webpart code is VSC editor.

  12. Coming back to command prompt window, we will use another command to see how the default WebPart looks like. For this we will use "gulp serve" command. In case of path issue, use the complete path

  13. This command executes a series of gulp tasks to create and start a local webserver hosting the endpoints localhost:4321 and localhost:5432. It will then open your default browser and load the workbench preview web parts from your local dev environment.

  14. 😟 Where is my webpart?
  15. Relax! Click on the + sign showing at the middle. It will populate the available webpart.

  16. Here is our webpart. Click on it. It will be added to the local workbench.

  17. Now, open your Sharepoint online site. change the root URL by removing the SitePages/<YourDefaultPage>.aspx by "_layouts/15/workbench.aspx" (https://yoursitename.sharepoint.com/sites/yoursubsitename/_layouts/15/workbench.aspx) and press Enter. (Remember WorkBench is available only in Online Site. It is not available in SharePoint 2016/2013...).

  18. This is your SharePoint site workbench. Now click on + sign at the middle. Surprisingly, It will show your webpart along with others.

  19. Click on it. It will be added to the SharePoint site workbench.

  20. Till now, we were creating our default SPFx webpart. Now we have to modify it so that we can try CRUD operations using it. For this, first we need a SharePoint list. I am using a simple list with 4 columns-
    1. Title : (or Full Name) of type Single Line of Text
    2. Address : of type Single Line of Text
    3. Mobile : of type Single Line of Text
    4. EmailID : of type Single Line of Text

  21. Fine. Till now you have done good job. Now, come to the code part. Remember, we had opened our SPFx webpart code in code editor using "code ." command.

  22. In Explorer window on left side, traverse down to "\src\webparts\crudOperationsBySpFxWebPart" and open "CrudOperationsBySpFxWebPartWebPart.ts" file.

  23. Look for the code line-
    1. import { escape } from '@microsoft/sp-lodash-subset';
       
    2. Add below line in next line (see line no.8 in below screenshot). SPHttpClient and SPHttpClientResponse will be used for REST API calls for CRUD operations.
    3. import { SPHttpClientSPHttpClientResponse } from'@microsoft/sp-http';

  24. Next look for below code-
    1. export interface IAzureWebApiWebPartWebPartProps {
        descriptionstring;
      }
    2. Add below interface just after it-
    3. interface IRegistrationDetails{
        Title:string;
        Address:string;
        Mobile:string;
        EmailID:string;
        Id:Int32Array;
      }
    4. The code will appear as below-

  25. Let's replace the default content view of webpart with some controls and a grid. Check the below line-
    1. export default class CrudOperationsBySpFxWebPartWebPart extends BaseClientSideWebPart<ICrudOperationsBySpFxWebPartWebPartProps> {

    2. Let's replace the highlighted content shown as below with the given next-

    3. Replace with below code-
    4. private Listnamestring = "Registration Details";
        private listItemIdnumber = 0;
        public render(): void {
          this.domElement.innerHTML = `
            <div>       
              <table>  
                <tr>  
                  <td>Full Name</td>            
                  <td><input type="text" id="idFullName" name="fullName" placeholder="Full Name.."></td>
                </tr>          
                <tr>            
                  <td>Address</td>            
                  <td><input type="text" id="idAddress" name="address" placeholder="Address.."></td>
                </tr>          
                <tr>            
                  <td>Mobile Number</td>            
                  <td><input type="text" id="idPhoneNumber" name="mobile" placeholder="Mobile Number.."></td>
                </tr>          
                <tr>            
                  <td>Email ID</td>            
                  <td><input type="text" id="idEmailId" name="emailid" placeholder="Email ID.."></td>
                </tr>
              </table>    
              <table>          
                <tr>            
                   <td>
                    <button class="${styles.button} find-Button">Find</button>
                    <button class="${styles.button} create-Button">Create</button>
                    <button class="${styles.button} update-Button">Update</button>
                    <button class="${styles.button} delete-Button">Delete</button>
                    <button class="${styles.button} clear-Button">Clear</button>
                  </td>
                </tr>
              </table>        
              <div id="tblRegistrationDetails"></div>
            </div>
           `;
          this.setButtonsEventHandlers();
          this.getListData();
    5. Here we have define 2 variables-
      1. Listname : It contains the name of the SharePoint list upon which we are going to perform CRUD operations.
      2. listItemId : it will contain the ID of the item we are going to Update/Delete/Read.  
    6.  There are 2 functions used here. These are going to be define next -
      1. setButtonsEventHandlers
      2. getListData
  26. In above HTML, we had displayed 4 input control "Full Name", "Address", "Mobile" and "Email ID".
  27. In all, 5 buttons have been added 
    1. Find : To get the record from list
    2. Create : To insert a new record in list
    3. Update : To update the existing record in list
    4. Delete : To delete the existing record from list
    5. Clear : To clear the input controls
  28. Next we will add function to assign EventListners to these buttons using below code. It will be added just after the closing bracket of above code-
  29.   private setButtonsEventHandlers(): void {
        const webPartCrudOperationsBySpFxWebPartWebPart = this;
        this.domElement.querySelector('button.find-Button').addEventListener('click', () => { webPart.find(); });
        this.domElement.querySelector('button.create-Button').addEventListener('click', () => { webPart.save(); });
        this.domElement.querySelector('button.update-Button').addEventListener('click', () => { webPart.update(); });
        this.domElement.querySelector('button.delete-Button').addEventListener('click', () => { webPart.delete(); });
        this.domElement.querySelector('button.clear-Button').addEventListener('click', () => { webPart.clear(); });
      }


  30. Next, add getListData function as below-
  31. private getListData() {
        let htmlstring = '<table border=1 width=100% style="border-collapse: collapse;">';
        html += `
                  <th>Full Name</th>
                  <th>Address</th>
                  <th>Email ID</th>
                  <th>Phone Number</th>
                  <th>Edit</th>
                  <th>Delete</th>
                `;
        this.context.spHttpClient.get(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items`SPHttpClient.configurations.v1)
          .then(response => {
            return response.json()
              .then((itemsany): void => {
                console.log('items.value: 'items.value);
                let listItemsIRegistrationDetails[] = items.value;
                console.log('list items: 'listItems);
     
                listItems.forEach((itemIRegistrationDetails=> {
                  html += `
                            <tr>
                              <td>${item.Title}</td>
                              <td>${item.Address}</td>
                              <td>${item.EmailID}</td>
                              <td>${item.Mobile}</td>
                              <td><button id="btnGetRecordData" class="${styles.button} getRecord-Button" data-val="${item.EmailID}">Edit</button></td>
                              <td><button id="btnDelRecordData" class="${styles.button} delRecord-Button" data-val="${item.Id}">Delete</button></td>
                              </tr>
                          `;
                });
                html += `</table>`;
                
                const listContainerElement = this.domElement.querySelector('#tblRegistrationDetails');
                listContainer.innerHTML = html;

                let editlistItems = document.getElementsByClassName("getRecord-Button");
                for(let j:number = 0j<editlistItems.lengthj++)
                {
                  editlistItems[j].addEventListener('click', (event=> {
                    this.GetRecordFromList(editlistItems[j].attributes["data-val"].value);
                  });
                }

                let dellistItems = document.getElementsByClassName("delRecord-Button");
                for(let j:number = 0j<dellistItems.lengthj++)
                {
                  dellistItems[j].addEventListener('click', (event=> {
                    this.DelRecordFromList(dellistItems[j].attributes["data-val"].value);
                  });
                }

              });
          });
      }



  32. Now we have to add functions that are need to be executed when user clicks on "Find/Create/Update/Delete/Clear"buttons. I am giving all 5 functions here as below. Just add after the getListData function.
  33.   private find(): void {
        let emailId = prompt("Enter the Email ID");
        this.context.spHttpClient.get(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items?$select=*&$filter=EmailID eq '${emailId}'`SPHttpClient.configurations.v1)
          .then(response => {
            return response.json()
              .then((itemany): void => {
                document.getElementById('idFullName')["value"] = item.value[0].Title;
                document.getElementById('idAddress')["value"] = item.value[0].Address;
                document.getElementById('idEmailId')["value"] = item.value[0].EmailID;
                document.getElementById('idPhoneNumber')["value"] = item.value[0].Mobile;
                this.listItemId = item.value[0].Id;
              });
          });
      }


  34.   private save(): void {
        const bodystring = JSON.stringify({
          'Title': document.getElementById('idFullName')["value"],
          'Address': document.getElementById('idAddress')["value"],
          'EmailID': document.getElementById('idEmailId')["value"],
          'Mobile': document.getElementById('idPhoneNumber')["value"],
        });
     
        this.context.spHttpClient.post(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items`,
          SPHttpClient.configurations.v1,
          {
            headers: {
              'Accept': 'application/json;odata=nometadata',
              'X-HTTP-Method': 'POST'
            },
            body: body
          }).then((responseSPHttpClientResponse): void => {
            this.getListData();
            this.clear();
            alert('Item has been successfully Saved ');
          }, (errorany): void => {
            alert(`${error}`);
          });
      }
  35.   private update(): void {
        const bodystring = JSON.stringify({
          'Title': document.getElementById('idFullName')["value"],
          'Address': document.getElementById('idAddress')["value"],
          'EmailID': document.getElementById('idEmailId')["value"],
          'Mobile': document.getElementById('idPhoneNumber')["value"],
        });
     
        this.context.spHttpClient.post(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items(${this.listItemId})`,
          SPHttpClient.configurations.v1,
          {
            headers: {
              'Accept': 'application/json;odata=nometadata',
              'IF-MATCH': '*',
              'X-HTTP-Method': 'PATCH'
            },
            body: body
          }).then((responseSPHttpClientResponse): void => {
            this.getListData();
            this.clear();
            alert(`Item successfully updated`);
          }, (errorany): void => {
            alert(`${error}`);
          });
      }
  36.   private delete(): void {
        if (!window.confirm('Are you sure you want to delete the this/latest item?')) {
          return;
        }
     
        this.context.spHttpClient.post(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items(${this.listItemId})`,
          SPHttpClient.configurations.v1,
          {
            headers: {
              'Accept': 'application/json;odata=nometadata',
              'IF-MATCH': '*',
              'X-HTTP-Method': 'DELETE'
            }
          }).then((responseSPHttpClientResponse): void => {
            alert(`Item successfully Deleted`);
            this.getListData();
            this.clear();
          }, (errorany): void => {
            alert(`${error}`);
          });
      }
  37.   private clear(): void {
        document.getElementById('idFullName')["value"] = "";
        document.getElementById('idAddress')["value"] = "";
        document.getElementById('idEmailId')["value"] = "";
        document.getElementById('idPhoneNumber')["value"] = "";
      }
  38. It will looks like as below-




  39. Two more functions needs to be added. These were executed when user will click on "Edit" / "Delete" button which are provided inline in grid.
  40.   private GetRecordFromList(emailId): void {
        this.context.spHttpClient.get(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items?$select=*&$filter=EmailID eq '${emailId}'`SPHttpClient.configurations.v1)
          .then(response => {
            return response.json()
              .then((itemany): void => {
                document.getElementById('idFullName')["value"] = item.value[0].Title;
                document.getElementById('idAddress')["value"] = item.value[0].Address;
                document.getElementById('idEmailId')["value"] = item.value[0].EmailID;
                document.getElementById('idPhoneNumber')["value"] = item.value[0].Mobile;
                this.listItemId = item.value[0].Id;
              });
          });
      }

  41.   private DelRecordFromList(Id): void {
        if (!window.confirm('Are you sure you want to delete this item?')) {
          return;
        }
     
        this.context.spHttpClient.post(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.Listname}')/items(${Id})`,
          SPHttpClient.configurations.v1,
          {
            headers: {
              'Accept': 'application/json;odata=nometadata',
              'IF-MATCH': '*',
              'X-HTTP-Method': 'DELETE'
            }
          }).then((responseSPHttpClientResponse): void => {
            alert(`Item successfully Deleted`);
            this.getListData();
            this.clear();
          }, (errorany): void => {
            alert(`${error}`);
          });
      }
  42. These will look as below-


  43. With this code part has been completed. Now come back to the workbench site. The SharePoint site where we had created the list "Registration Details". For this the best way is to go to the SharePoint list as below-

  44. Now replace the visible part of URL in above screenshot with "/_layouts/15/workbench.aspx" 
  45. It will look like as below-

     
  46. In case, if the webpart is not displayed, please follow steps 17,18,19 to add the webpart in workbench.
  47. Now, let's add some records.

  48. Now click on Create button. Record will be inserted in SharePoint list and a confirmation popup will be displayed. Grid will be refreshed.


  49. Now, we have 2 options to edit any record.
    1. Click on "Find" button. A popup will appear for Email input. Provide the value and click on OK. It will populate the record in respective fields. Now make changes and click on Update button.
    2. Click on Edit button provided in grid ahead of each record. It will populate the record in respective fields. Now make changes and click on Update button.
  50. Similarly, for deletion-
    1. Click on "Find" button. A popup will appear for Email input. Provide the value and click on OK. It will populate the record in respective fields. Now click on Delete button.
    2. Click on Delete button provided in grid ahead of each record. It will delete the record.
  51. Clear button is to reset the input fields.
  52. I know, you are still curious about that record whether it has been finally saved in SharePoint list or not. Let's check the SharePoint list-

  53. There may be chance that when open your SharePoint site workbench page, you may get something weird as below screenshot. Don't panic, this may appear due to the reason that you haven't executed "gulp serve" command. Please verify if gulp is running. If it is running and you still facing the issue, please stop the gulp using Ctrl+C (then press Y to terminate batch job), close all browsers. Then again start gulp by using command "gulp serve". It will open your local workbench in browser. Now open your SharePoint site workbench in another tab and now you will find your SPFx webpart there. 


This way, we can make CRUD operations using SPFx webpart.
  1. Refer more details upon SPFx WebPart, please visit Microsoft Website.

Update (Date: 14 June 2021):-
  1. In case, if you wish to get the ID of the record created using Save command, it might be possible you won't get here. for this, you need to update "odata=nometadata" with "odata.metadata=minimal".
  2. Further, you need to create a .ts file with name IListItem. Define an interface with all the columns of list in this interface.
  3. Import this interface in ...WebPart.ts file.
  4. Then update the existing code with below one-
  5. this.context.spHttpClient.post(`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.properties.listName}')/items`,
          SPHttpClient.configurations.v1,
          {
            headers: {
              'Accept': 'application/json;odata.metadata=minimal',
              'Content-type': 'application/json;odata.metadata=minimal',  
              'X-HTTP-Method': 'POST'
            },
            body: body
          }).then((response: SPHttpClientResponse): Promise<IListItem> => {
            return response.json();
          })
          .then((item:IListItem):void=>{
            alert(`Item '${item.Name}' (ID: ${item.ID}) successfully created`)
          }, (error: any): void => {
            alert(`${error}`);
          });
    
  6. The process is explained in my ReactJS post. 

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.