Custom Sharepoint Form using SPFx (No Javascript Framework)

In a recent requirement for a client, I had to develop a custom form as a web part using SPFx. The form required basic inputs like textboxes, Textarea, cascading dropdowns from master lists, date picker, and people picker, etc. The requirement was straightforward and seemed noncomplex at first. But as it was my very first encounter with the SPFx framework, I had a few hiccups on the way in getting the form done.
SPFx let you build client-side web part for SharePoint online leveraging modern technologies

Are you a SharePoint developer looking to build beyond “Hello World” SPFx client-side web-part?

Client-side web parts are client-side components that run inside the context of a SharePoint page. Client-side web parts can be deployed to SharePoint Online, and you can also use modern JavaScript tools and libraries to build them.

What is SPFx?

The SharePoint Framework (or SPFx) is a new development model for SharePoint user interface extensibility. It is used by first and third parties, complementing already existing user interface models such as the SharePoint Add-in model. The SharePoint Framework allows for a structured and supported approach to enrich and extend the user interface of SharePoint, by using client-side frameworks with initial support for client-side web parts. Based on modern web technology standards, it offers a unique set of features to make SharePoint customizations more broadly available for developers and enterprises, but at the same time aligns with previous models and patterns in SharePoint. 

Within multi-tenant SharePoint Online, full trust code has never been supported, and the sandboxed code service has been deprecated. The most common patterns for customizations of SharePoint Online have been either through add-ins, remote-code execution (code executing elsewhere, such as in Azure) through the standard APIs, and JavaScript embedding. Although JavaScript embedding has been a very powerful way of extending SharePoint, it has also proven difficult to keep up with the evergreen model of SharePoint Online. The SharePoint Framework aims to solve these issues by providing a standardized framework on how to create custom user interface extensions as well as building applications on top of SharePoint Online in a supported and future prepared way.

(courtesy: Enterprise Guidance)

When done right, SharePoint development is fun. This post is my first attempt to contribute to the ecosystem with my insights and learnings. The use of SPFx for user interface extensibility is fun. Like every new SharePoint developer, I struggled a bit initially to get the ‘Forms’ working – but, thanks to guidance documentation the first form using SPFx is out there working.

In a recent requirement for a client, I had to develop a custom form as a web part using SPFx. The form required basic inputs like textboxes, Textarea, cascading dropdowns from master lists, date picker, and people picker, etc. The requirement was straightforward and seemed noncomplex at first. But as it was my very first encounter with the SPFx framework, I had a few hiccups on the way in getting the form done.

In this post, I will try to take you through the process of developing a form using SPFx (No JavaScript Framework option). I plan on getting a form done using ReactJS or KnockoutJS in the near future.

So, let’s get started…

System Requirements:

  • Node .js
  • Npm package manager
  • Yeoman and gulp

Install and setup the environment as mentioned in this link.

Setup your Project;

  • Create Project Folder

md TestForm

  • Go to project folder

cd TestForm

  • Create a new web part by running the Yeoman SharePoint Generator

yo @microsoft/SharePoint

  • When prompted, select/fill as follows:
  • After this, it takes some time for yeoman to setup the project depending on the connection speed.
  • Once the project is setup successfully, you should see a screen similar to the one below:
  • Now you can use any code editor of your choice to edit the webpart code. I prefer using Visual Studio Code for the purpose. It can be downloaded from this link.
  • Install gulp dev certificate (This needs to done only once in your system)

gulp trust-dev-cert

  • After the dev certificate is installed, run the following to start a local node server with an instance of SharePoint workbench to deploy and preview the webpart.

gulp serve

  • Once the workbench is up, click on the Add button to open the webparts list available and select your web part to add to the page.
  • Add Webpart for page preview
  • To open the webpart for preview directly in your SharePoint tenant, go to:

https://<<Sharepoint site URL>>/_layouts/15/workbench.aspx

This will open an instance of then SharePoint workbench like the one above and you can add your webpart there to directly interact with your SharePoint lists and libraries. But unlike the local workbench which automatically refreshes each time the page code changes (mind you, gulp serve should be running), the SharePoint workbench must be manually refreshed to see changes.

  • Let’s start customizing our webpart.

Test Webpart Requirements:

  • Custom form
  • Textbox for Activity Name
  • Date picker for activity date
  • People picker control for selecting user assigned with activity
  • 2 dropdowns for selecting Category and Sub Category

Customizing the webpart:

  • Open the solution using Visual Studio Code:
  • I won’t be going through the details of the solution structure and so on. Details on that can be found here.
  • For our webpart we will be using jQuery, Bootstrap (for UI) and sp-pnp.js for interaction with SharePoint lists.
  • Install sp-pnp.js using:

npm install sp-pnp-js –save

  • Install jquery using :

npm install –save @types/jquery@3 -D

  • We also need the following for our webpart:
    • Sp.peoplepicker.js and other required files from here
  • Create a folder for css and one for scripts under src/webparts/testform and copy all css and js files into the respective folders.

These directories with files will be copied automatically to lib under same hierarchy when project is built for distribution.

Open config.json under config and add the following in externals attribute:

 "externals": {
    "jquery": {
      "path": "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js",
      "globalName": "jquery"
    },
    "bootstrap": {
      "path": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js",
      "globalName": "bootstrap",
      "globalDependencies": ["jquery"]
    },
    "appjs": {
      "path": "lib/webparts/testForm/scripts/app.js",
      "globalName": "appjs"
    },
    "sppeoplepicker": {
      "path": "lib/webparts/testForm/scripts/sp.peoplepicker.js",
      "globalName": "sppeoplepicker"
    },
    "jqueryui": {
      "path": "lib/webparts/testForm/scripts/jquery-ui.js",
      "globalName": "jqueryui"
    }
  }

  • If gulp serve is running, stop and restart as any changes in config.json need gulp serve to be rerun .
  • Open TestFormWebPart.ts file under src/webparts/testForm and add the following lines to import various dependencies:
import { SPComponentLoader } from '@microsoft/sp-loader';
import pnp, { sp, Item, ItemAddResult, ItemUpdateResult, Web } from 'sp-pnp-js';
import * as $ from 'jquery';
require('bootstrap');
require('./css/jquery-ui.css');
let cssURL = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
SPComponentLoader.loadCss(cssURL);
SPComponentLoader.loadScript("https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js");
require('appjs');
require('sppeoplepicker');
require('jqueryui');
  • Also modify the sp-core library import to fetch URL parameters, if required:
import { UrlQueryParameterCollection, Version } from '@microsoft/sp-core-library';
  • Overwrite the render() method with the following:
public render(): void {
    this.domElement.innerHTML = `
    <div id="container" class="container">
     
    <div class="panel">
      <div class="panel-body">
        <div class="row">
          <div class="col-lg-4 control-padding">
            <label>Activity</label>
            <input type='textbox' name='txtActivity' id='txtActivity' class="form-control" value="" placeholder="" >
          </div>
          <div class="col-lg-4 control-padding">
            <label>Activity Performed By</label>
           
              <div id="ppDefault"></div>
          </div>
          <div class="col-lg-4 control-padding">
            <label>Activity Date</label>
            <div class="input-group date" data-provide="datepicker">
      <input type="text" class="form-control" id="txtDate" name="txtDate">
  </div>
          </div>          
        </div>
  
        <div class="row">
        <div class="col-lg-6 control-padding">
            <label>Category</label>
            <select name="ddlCategory" id="ddlCategory" class="form-control">
  
            </select>
          </div>
          <div class="col-lg-6 control-padding">
            <label>Sub Category</label>
            <select name="ddlSubCategory" id="ddlSubCategory" class="form-control">
  
            </select>
          </div>         
        </div>       
  
        <div class="row">
        <div class="col col-lg-12">
        <button type="button" class="btn btn-primary buttons" id="btnSubmit">Save</button>
        <button type="button" class="btn btn-default buttons" id="btnCancel">Cancel</button>
      </div>
        </div>
  
      </div>
    </div>`;

    (<any>$("#txtDate")).datepicker(
      {
        changeMonth: true,
        changeYear: true,
        dateFormat: "mm/dd/yy"
      }
    );
    (<any>$('#ppDefault')).spPeoplePicker({
      minSearchTriggerLength: 2,
      maximumEntitySuggestions: 10,
      principalType: 1,
      principalSource: 15,
      searchPrefix: '',
      searchSuffix: '',
      displayResultCount: 6,
      maxSelectedUsers: 1
    });
    this.AddEventListeners();
   this.getCategoryData();

  }

In the render() method, the following code, initializes the datepicker control:

(<any>$("#txtDate")).datepicker(
      {
        changeMonth: true,
        changeYear: true,
        dateFormat: "mm/dd/yy"
      }
    );

And the following code initializes the people picker:

(<any>$('#ppDefault')).spPeoplePicker({
      minSearchTriggerLength: 2,
      maximumEntitySuggestions: 10,
      principalType: 1,
      principalSource: 15,
      searchPrefix: '',
      searchSuffix: '',
      displayResultCount: 6,
      maxSelectedUsers: 1
    });

Note that I have used <any> before initializing both the controls.  I found in SPFx that if I try to initialize them without <any> it gave “method not found” error.

  • Implement AddEventListeners() method to attach event listeners to controls:
private AddEventListeners(): any {
    document.getElementById('btnSubmit').addEventListener('click', () => this.SubmitData());
    document.getElementById('btnCancel').addEventListener('click', () => this.CancelForm());   
    document.getElementById('ddlSysWorked').addEventListener('change', () => this.PopulateSubCategory());
  }
  • Implement getCategoryData() method to populate category dropdown from master list.
private _getCategoryData(): any {    
    return pnp.sp.web.lists.getByTitle("Category").items.select("Category").getAll().then((response) => {
      return response;
    });
  }

  private getCategoryData(): any {
    this._getCategoryData()
      .then((response) => {
        this._renderCategoryList(response);
      });
  }

  private _renderCategoryList(items: any): void {

    let html: string = '';
    html += `<option value="Select Category" selected>Select Category</option>`;
    items.forEach((item: any) => {
      html += `
       <option value="${item.Category}">${item.Category}</option>`;
    });
    const listContainer1: Element = this.domElement.querySelector('#ddlCategory');
    listContainer1.innerHTML = html;
  }
  • On change event of category should populate the subcategory dropdown control with requisite values from master list. Added the change listener on category dropdown to call PopulateSubCategory() method.
public PopulateSubCategory() {
    this.getSubCategoryData($("#ddlCategory").val().toString());
  }

  private _getSubCategoryData(category): any {    
    return pnp.sp.web.lists.getByTitle("SubCategory").items.select("SubCategory").filter("Category eq '" + category + "'").getAll().then((response) => {
      return response;
    });
  }

  private getSubCategoryData(category): any {
    this._getSubCategoryData(category)
      .then((response) => {
        this._renderSubCategoryList(response);
      });
  }

  private _renderSubCategoryList(items: any): void {

    let html: string = '';
    html += `<option value="Select Sub Category" selected>Select Sub Category</option>`;
    items.forEach((item: any) => {
      html += `
       <option value="${item.SubCategory}">${item.SubCategory}</option>`;
    });
    const listContainer1: Element = this.domElement.querySelector('#ddlSubCategory');
    listContainer1.innerHTML = html;
  }
  • Now that the form is created and initialized with controls, we need to implement the Submit and Cancel functionalities.
  • To implement Cancel, we will read the URL from where this page was opened, search for the Source attribute and redirect the page there.
private CancelForm() {
    window.location.href = this.GetQueryStringByParameter("Source");
  }

  private GetQueryStringByParameter(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
      results = regex.exec(location.search);
    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }
  • Finally, let’s implement the Submit functionality and save the data to the Sharepoint list.
private SubmitData(){
  var userinfo = (<any>$('#ppDefault')).spPeoplePicker('get');
  var userId;
  var userDetails = this.GetUserId(userinfo[0].email.toString());
  console.log(JSON.stringify(userDetails));
  userId = userDetails.d.Id;

  pnp.sp.web.lists.getByTitle('RigActiveList_Job_Cards_Area').items.add({
    Title: "Test",
    Activity: $("#txtActivity").val().toString(),
    Activity_Date: $("#txtDate").val().toString(),
    Activity_ById : userId,
    Category: $("#ddlCategory").val().toString(),
    SubCategory: $("#ddlSubCategory").val().toString(),
});
}
private GetUserId(userName) {
  var siteUrl = this.context.pageContext.web.absoluteUrl;

  var call = $.ajax({
    url: siteUrl + "/_api/web/siteusers/getbyloginname(@v)?@v=%27i:0%23.f|membership|" + userName + "%27",
    method: "GET",
    headers: { "Accept": "application/json; odata=verbose" },
    async: false,
    dataType: 'json'
  }).responseJSON;
  return call;
}

Note that I have used a separate GetUSerID method. The sp.peoplepicker.js control returns an userinfo object and not the Id of the user selected. But to save the user into SharePoint list as a person or group object, we need the Id. So I am passing the userinfo object to the GetUserId method and returning the Id to be saved to Sharepoint list.

This is the final layout of the form:

That’s about it guys. That’s how I managed to get my first SPFx webpart up and running.

I hope it helps others looking for similar solutions. I will be working on SPFx with ReactJs and posting the solution soon. Have a great time coding.

The project is available as a GitHub repository here.

Previous ArticleNext Article

64 Comments

    1. Hi Madhan,
      Thanks for reaching out.
      The app.js is part of the SpPeoplePicker package. The same can be found here

    1. getting error $ jQuery is not defined and spPeoplePicker is not a function in case of people picker.

      1. Hi Nalini,

        Thanks for reaching out. The jquery error comes if the location of jquery in config.json is not resolved.
        If jquery is not resolved, people picker also does not work as its is dependent on jquery.
        If you still have issues, please reach ot with specif errors and code snippets/logs/screenshots, if possible.

        Thanks

    2. Hi Nalini,

      Thanks for reaching out. The people picker code is available in the source only. The entire source code can be found in the Github repository link : https://github.com/adreno-abhi/SPFx-NoJavascript-Form
      The integration is explained in the blog.
      If you are still having issues, please reach out with the error specifics and logs/screenshots if possible.

  1. Hi, I am also building a custom form for a list using spfx with react.
    Did you already build anything to control de type of the form? (edit form, new form, disp form)
    Any ideas on how would be the best solution for that?
    I am also wondering how i’m going to add the custom form to the list.

    1. Hi Felipe,

      Thanks for reaching out. We will be publishing a separate blog for SPFx form using React.

      Thanks

    2. Hi Felipe,
      Did you find anything to control the type of the form? (edit form, new form, disp form). If Yes, Please give the reference.

      1. Hi,

        The way i manage new,edit and view is somewhat like this:
        1. In case of edit and view add Sharepoint Item ID and Action = view/edit in url parameter
        2. On page load, if url does not contain id, use the form as new, all controls are defaulted and submit button calls the submit function which adds new item to list.
        3. In case id is present in URL, check the action
        4. If action is edit, fetch data for the id using pnp and populate the form using the retrieved data. The submit button calls the update function that updates the item with the id passed in url.
        5. If action is view, fetch data for the id using pnp and populate the form using the retrieved data. All controls are made read only. Submit button is disabled or hidden

        1. Hi,
          Thanks for your replay.
          I would like to know that your custom form working in Modern View or Classic View?.

          If it is modern view, how i can configure new/edit item url from all item page?

  2. This is really cool. Unfortunately I am getting the following error on the People Picker:

    Uncaught Sys.ArgumentNullException: Sys.ArgumentNullException: Value cannot be null.
    Parameter name: serverRelativeUrlOrFullUrl

    1. Hi K. Bailey,

      Thanks for reaching out. Can you please share the error log and file where this error occurs. Although its not very clear from your issue described here, you can probably try to set the hostUrl and appWebUrl variable values to your Sharepoint site URL to check if it solves.
      Else please provide a complete detail/log of the issue to help fix your issue.

      Thanks

      1. Hi Arjun,

        Thanks for reaching out.
        Can you try to make the following adjustments to the app.js file : TestForm/src/webparts/testForm/scripts/app.js

        Change
        ——-
        var hostUrl = $app.getUrlParamByName(‘SPHostUrl’);
        var appWebUrl = $app.getUrlParamByName(‘SPAppWebUrl’);

        To
        —-
        var hostUrl = ‘Your Sharepoint site collection URL’
        var appWebUrl = ‘Your Sharepoint site collection URL’;

        It seems the URL could not be fetched automatically in your case.

        Thanks

          1. Hi Gowri Shankar,

            Since people picker control searches from the users list in a sharepoint site, localhost workbench wont work. This is a default behaviour that you need to use the tenant workbench to test people picker search.

          2. It worked on development but not on production. I am not even getting any error. How to trace the issue?

          3. Hi Gowri Shankar
            You can enable developer console by pressing F12 in Chrome and then checking for error logs in Console. If you can share those error logs here it will help check the error.

          4. i have the same problem. Change app.js
            var hostUrl = window.location.protocol + “//” + window.location.host;
            var appWebUrl = window.location.protocol + “//” + window.location.host;
            and now is ok …

  3. Hi Abhijeet,

    I am trying to search when applying same code in sharepoint online workbench but it is not populating any name from its address book.

  4. After following the steps, I get the error:

    [SPLoaderError.loadComponentError]:
    ***Failed to load component “5bd183e3-dcb9-4792-aab7-be7993b057ab” (HelloWorldWebPart). Original error: ***Failed to load path dependency “appjs” from component “5bd183e3-dcb9-4792-aab7-be7993b057ab” (HelloWorldWebPart). Original error: Error loading https://component-id.invalid/5bd183e3-dcb9-4792-aab7-be7993b057ab_0.0.1/appjs
    Evaluating https://localhost:4321/src/webparts/helloWorld/scripts/app.js
    appjs is not defined

    1. have you got anything i am facing same issue appjs is not working for me. i tried to load with require(‘./scripts/app’); statment in webpart file then its loading on if i check in chrome but now it is throwing error as $app is not defined. one line in sppeoplepicker.js is calling function of appjs there it is failing. let me know if this works for you.

      1. Hi Harsh,

        This error usually occurs if the appjs/peoplepicker get loaded before jquery. To fix this, add :
        “globalDependencies”: [“jquery”]
        in config json for both appjs and sppeoplepicker, so that until jquery is loaded, these are not loaded.
        This should fix the issue.
        I have updated the config.json screenshot of the blog as well as the github repo to reflect the changes.
        Thanks

    2. Hi Manisha,

      This error usually occurs if the appjs/peoplepicker get loaded before jquery. To fix this, add :
      “globalDependencies”: [“jquery”]
      in config json for both appjs and sppeoplepicker, so that until jquery is loaded, these are not loaded.
      This should fix the issue.
      I have updated the config.json screenshot of the blog as well as the github repo to reflect the changes.
      Thanks

  5. I’m running into some issues with the webpart in production. It works fine in the workbench except I’m not able to submit.
    In prod, it’s not able to retrieve the categories and subcategories. I’m getting this error in the console: Failed to load resource: the server responded with a status of 404 ()

    It seems like the URL it’s trying to use in the getCategoryData method use is this:
    ‘https://mytenant.sharepoint.com/sites/MySite/SitePages/_api/web/lists/getByTitle(‘Categories’)/items?$top=2000&$select=Title’

    Instead of this:
    ‘https://mytenant.sharepoint.com/sites/MySite/_api/web/lists/getByTitle(‘Categories’)/items’

    It’s adding “SitePages” in there, which would in fact make it nonexistent.

    Any suggestions?

    1. HI JB,

      To have pnp make calls to a specific site, you can try this.

      1. Create a Web object for the url you wish to connect to in the function you are making the pnp call:
      let w = new Web(“YOur_URL_Here”);

      2. Now instead of :
      pnp.sp.web.lists.getByTitle(“ListName”)………

      use
      w.lists.getByTitle(“ListName”)……..

      That should get you connected to the correct url.
      Do let me know if it fixed you issue or not.

      Thanks

        1. Hi JB,

          Glad that your earlier issue got resolved. From the logs you shared it seems you are getting a 4004 error and not 404. 404 would have meant the resource is not available. But thats not the case from the logs. Error 400 means – Bad Request. It usually could mean that the JSON that you are passing to add() is not as per the list structure. Please mind that the name of columns should be internal column name and not the name you see. I would double check the column names (internal) and data types being passed to eliminate causes of issues.
          Do let us know if that helps.

          Thanks

  6. Hi Abhijeet,

    People Picker is not getting resolved i am getting the below error.

    Error:
    VM3419:2 Uncaught Sys.ArgumentNullException: Sys.ArgumentNullException: Value cannot be null.
    Parameter name: serverRelativeUrlOrFullUrl
    at Function.Error.create (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:2736)
    at Function.Error.argumentNull (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:3200)
    at SP.ClientContext.SP.ClientRuntimeContext (:2:54363)
    at Function.Type.initializeBase (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:8478)
    at new SP.ClientContext (:2:24034)
    at Object. (https://localhost:4321/lib/webparts/riskwebpart/scripts/app.js:22:46)
    at c (https://code.jquery.com/jquery-3.5.1.min.js:2:28294)
    at Object.fireWith [as resolveWith] (https://code.jquery.com/jquery-3.5.1.min.js:2:29039)
    at l (https://code.jquery.com/jquery-3.5.1.min.js:2:79800)
    at XMLHttpRequest. (https://code.jquery.com/jquery-3.5.1.min.js:2:82254)

    1. Thanks for reaching out.
      Can you try to make the following adjustments to the app.js file

      Change
      ——-
      var hostUrl = $app.getUrlParamByName(‘SPHostUrl’);
      var appWebUrl = $app.getUrlParamByName(‘SPAppWebUrl’);

      To
      —-
      var hostUrl = ‘Your Sharepoint site collection URL’
      var appWebUrl = ‘Your Sharepoint site collection URL’;

      It seems the URL could not be fetched automatically in your case.

      1. Hi Abhijeet,

        Thanks for the response eventhough i have changed the url as well in the app.js file even though i am getting the error.

        Step 1 : app.js i have changed the url

        Step 2 :
        import * as $ from ‘jquery’;
        require(‘appjs’);
        require(‘sppeoplepicker’);
        require(‘jqueryui’);

        ($(‘#ppDefault’)).spPeoplePicker({
        minSearchTriggerLength: 2,
        maximumEntitySuggestions: 10,
        principalType: 1,
        principalSource: 15,
        searchPrefix: ”,
        searchSuffix: ”,
        displayResultCount: 6,
        maxSelectedUsers: 1
        });

        But i couldnt able to replicate the same behaviour.

        1. Hi,

          If possible can you share the app.js code snippet you changed and the full error message maybe on a online notepad and share link. Also i hope you added jquery as global dependency in config.json as was mentioned in an edit to blog as well as previous comments.
          Lastly are you testing on local workbench or sharepoint tenant workbench. People picker wont work on localhost.

  7. Hi Abhijeet,

    I am using in sharepoint workbench only abhijeet, for screenshots how can i share ?

    kindly assist me on this abhijeet.

    Config.json – i have add as global dependency code is shared below.

    “externals”: {
    “jquery”: {

    “path”: “https://code.jquery.com/jquery-3.5.1.min.js”,
    “globalName”: “jquery”
    },
    “bootstrap”: {
    “path”: “https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js”,
    “globalName”: “bootstrap”,
    “globalDependencies”: [“jquery”]
    },
    “appjs”: {
    “path”: “./lib/webparts/rswebpart/scripts/app.js”,
    “globalName”: “appjs”
    },
    “sppeoplepicker”: {
    “path”: “./lib/webparts/rswebpart/scripts/sp.peoplepicker.js”,
    “globalName”: “sppeoplepicker”
    },
    “jqueryui”: {
    “path”: “./lib/webparts/rswebpart/scripts/jquery-ui.js”,
    “globalName”: “jqueryui”
    }
    },

    app.js

    var $app = {

    onStartPromise: null,
    getContextPromise: null,
    spContext: null,

    // using jQuery promises to load a single shared spcontext across a client application
    withSPContext: function (action) {

    if ($app.getContextPromise == null) {
    $app.getContextPromise = $.Deferred(function (def) {

    var hostUrl = $app.getUrlParamByName(‘mysitecollurl’);
    var appWebUrl = $app.getUrlParamByName(‘mysitecollurl’);

    var scriptbase = hostUrl + ‘/_layouts/15/’;

    $.getScript(scriptbase + ‘SP.Runtime.js’).done(function () {
    $.getScript(scriptbase + ‘SP.js’).done(function () {
    $.getScript(scriptbase + ‘SP.RequestExecutor.js’).done(function () {

    $app.spContext = new SP.ClientContext(appWebUrl);
    var factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl);
    $app.spContext.set_webRequestExecutorFactory(factory);

    def.resolveWith($app.spContext, [$app.spContext]);
    });
    });
    });

    }).promise();
    }

    if ($.isFunction(action)) {
    $app.getContextPromise.done(action);
    }

    return $app.getContextPromise;
    },

    getUrlParamByName: function (name) {
    name = name.replace(/[\[]/, ‘\\[‘).replace(/[\]]/, ‘\\]’);
    var regex = new RegExp(‘[\\?&]’ + name + ‘=([^&#]*)’);
    results = regex.exec(location.search);
    return results == null ? ” : decodeURIComponent(results[1].replace(/\+/g, ‘ ‘));
    },

    getCtxCallback: function (context, method) {

    var args = [].slice.call(arguments).slice(2);

    return function () {
    method.apply(context, args);
    }
    },

    defaultProgramErrorHandler: function (jqXHR, textStatus) {
    textStatus = ” + textStatus;
    console.error(textStatus);
    alert(‘There was a problem executing your request. Please try again. If the problem persists please relaunch the app or refresh the page.’);
    },

    appendSPQueryToUrl: function (/*string*/ url) {

    // we already have the SPHostUrl param from somewhere else, just give back the url
    if (url.indexOf(‘SPHostUrl=’) > -1) {
    return url;
    }

    // add the required parameters
    url += url.indexOf(‘?’) > -1 ? ‘&’ : ‘?’;
    url += ‘SPHostUrl=’ + encodeURIComponent($app.getUrlParamByName(‘mysitecollurl’));
    url += ‘&SPAppWebUrl=’ + encodeURIComponent($app.getUrlParamByName(‘mysitecollurl’));
    url += ‘&SPLanguage=’ + encodeURIComponent($app.getUrlParamByName(‘SPLanguage’));
    url += ‘&SPClientTag=’ + encodeURIComponent($app.getUrlParamByName(‘SPClientTag’));
    url += ‘&SPProductNumber=’ + encodeURIComponent($app.getUrlParamByName(‘SPProductNumber’));

    return url;
    },

    getAuthorityFromUrl: function (/*string*/ url) {
    if (url) {
    var match = /^(?:https:\/\/|http:\/\/|\/\/)([^\/\?#]+)(?:\/|#|$|\?)/i.exec(url);
    if (match) {
    return match[1].toUpperCase();
    }
    }
    return null;
    },

    ensureContextQueryString: function () {

    // remove the redirect flag
    var SPHasRedirectedToSharePointParam = “&SPHasRedirectedToSharePoint=1”;
    var queryString = window.location.search;
    if (queryString.indexOf(SPHasRedirectedToSharePointParam) >= 0) {
    window.location.search = queryString.replace(SPHasRedirectedToSharePointParam, “”);
    }

    $app.ensureSPHostUrlInLinks($(‘a’));
    },

    ensureSPHostUrlInLinks: function (/*jquery*/ parentNode) {

    var currentAuthority = $app.getAuthorityFromUrl(window.location.href);

    parentNode.filter(function () {
    var authority = $app.getAuthorityFromUrl(this.href);
    if (!authority && /^#|:/.test(this.href)) {
    // Filters out anchors and urls with other unsupported protocols.
    return false;
    }
    return authority != null && authority.toUpperCase() == currentAuthority;
    }).each(function () {
    this.href = $app.appendSPQueryToUrl(this.href);
    });
    },

    onStart: function (/*function()*/ onStartFunc) {

    if ($app.onStartPromise == null) {
    $app.onStartPromise = $.when($.Deferred(function (d) { $(d.resolve); }).then($.Deferred(function (d) { $app.ensureContextQueryString(); d.resolve(); }))).promise();
    }

    if ($.isFunction(onStartFunc)) {
    $app.onStartPromise.done(onStartFunc);
    }

    return $app.onStartPromise;
    },

    combinePaths: function () {
    var parts = [];
    for (var i = 0; i < arguments.length; i++) {
    parts.push(arguments[i].replace(/^[\\|\/]/, '').replace(/[\\|\/]$/, ''));
    }
    return parts.join("/").replace(/\\/, '/');
    }
    };

    1. Try adding the
      “globalDependencies”: [“jquery”]
      in appjs and peoplepicker and jqueryui as well.
      You can share screenshots by uploading to some public sharing site and share link here

  8. Hi Abhijeet,

    Regarding the error detailsi have shared below

    Exception :

    Uncaught Sys.ArgumentNullException: Sys.ArgumentNullException: Value cannot be null.
    Parameter name: serverRelativeUrlOrFullUrl
    at Function.Error.create (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:2736)
    at Function.Error.argumentNull (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:3200)
    at SP.ClientContext.SP.ClientRuntimeContext (:2:54363)
    at Function.Type.initializeBase (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:8478)
    at new SP.ClientContext (:2:24034)
    at Object. (https://localhost:4321/lib/webparts/riskwebpart/scripts/app.js:22:46)
    at c (https://code.jquery.com/jquery-3.5.1.min.js:2:28294)
    at Object.fireWith [as resolveWith] (https://code.jquery.com/jquery-3.5.1.min.js:2:29039)
    at l (https://code.jquery.com/jquery-3.5.1.min.js:2:79800)
    at XMLHttpRequest. (https://code.jquery.com/jquery-3.5.1.min.js:2:82254)
    Error.create @ MicrosoftAjax.js:5
    Error.argumentNull @ MicrosoftAjax.js:5
    SP.ClientRuntimeContext @ VM6098:2
    Type.initializeBase @ MicrosoftAjax.js:5
    SP.ClientContext @ VM6099:2
    (anonymous) @ app.js:22
    c @ jquery-3.5.1.min.js:2
    fireWith @ jquery-3.5.1.min.js:2
    l @ jquery-3.5.1.min.js:2
    (anonymous) @ jquery-3.5.1.min.js:2
    load (async)
    send @ jquery-3.5.1.min.js:2
    ajax @ jquery-3.5.1.min.js:2
    S. @ jquery-3.5.1.min.js:2
    getScript @ jquery-3.5.1.min.js:2
    (anonymous) @ app.js:20
    c @ jquery-3.5.1.min.js:2
    fireWith @ jquery-3.5.1.min.js:2
    l @ jquery-3.5.1.min.js:2
    (anonymous) @ jquery-3.5.1.min.js:2
    load (async)
    send @ jquery-3.5.1.min.js:2
    ajax @ jquery-3.5.1.min.js:2
    S. @ jquery-3.5.1.min.js:2
    getScript @ jquery-3.5.1.min.js:2
    (anonymous) @ app.js:19
    c @ jquery-3.5.1.min.js:2
    fireWith @ jquery-3.5.1.min.js:2
    l @ jquery-3.5.1.min.js:2
    (anonymous) @ jquery-3.5.1.min.js:2
    load (async)
    send @ jquery-3.5.1.min.js:2
    ajax @ jquery-3.5.1.min.js:2
    S. @ jquery-3.5.1.min.js:2
    getScript @ jquery-3.5.1.min.js:2
    (anonymous) @ app.js:18
    Deferred @ jquery-3.5.1.min.js:2
    withSPContext @ app.js:11
    searchUser @ sp.peoplepicker.js:128
    dispatch @ jquery-3.5.1.min.js:2
    v.handle @ jquery-3.5.1.min.js:2

  9. Hi Abhijeet,

    For the people picker rendering is not uniform. getting this error frequently

    sp.peoplepicker.js:311 Uncaught ReferenceError: jQuery is not defined
    at sp.peoplepicker.js:311
    (anonymous) @ sp.peoplepicker.js:311

    WebPart.ts:330 Uncaught TypeError: jquery__WEBPACK_IMPORTED_MODULE_12__(…).spPeoplePicker is not a function
    at WebPart.aJvA.RiskwebpartWebPart.CustomSearch (WebPart.ts:330)
    at HTMLButtonElement. (WebPart.ts:105)

    1. Hi Sai,
      The behaviour mentioned by you happens if people picker is loaded before jquery has loaded in form. To rectify this add jquery as global dependency for all external attributes as shown in blog screenshot under config.json setup.
      This should solve the issue.

  10. Hi Abhijeet,

    Many thanks for the response provided, i have been update the dependency now its rendering fine.

    I would like to render the people picker inside textbox itself instead of appending it to div could you please let me know how we can done this?

    1. HI Sai,

      Glad it worked.
      Currently in No Javascript Framework, this is the implementation of people picker that we have done. We will be publishing a separate blog with office ui fabric and sp pnp react controls using the React Framework sometime in future.

  11. I’m having trouble getting the various dependencies to work in my webpart. I’ve built a few forms already using your method and now, out of nowhere, I’m getting the error below with all of my dependencies (bootstrap, appjs, sppeoplepicker and jqueryui):

    [SPLoaderError.loadComponentError]:
    Failed to load component “9576cd25-8147-4f69-b896-5513b23ef436” (EquipmentAccessFormWebPart). Original error: Failed to load path dependency “sppeoplepicker” from component “9576cd25-8147-4f69-b896-5513b23ef436” (EquipmentAccessFormWebPart) due to another dependency that failed to load.

    INNERERROR:
    Failed to load path dependency “sppeoplepicker” from component “9576cd25-8147-4f69-b896-5513b23ef436” (EquipmentAccessFormWebPart) due to another dependency that failed to load.
    CALLSTACK:
    Error
    at r.t [as constructor] (https://spoprod-a.akamaihd.net/files/sp-client/sp-webpart-workbench-assembly_en-us_d838220e0a8e0ac1c0d8c6b3cd80d4e2.js:1:410739)
    at new r (https://spoprod-a.akamaihd.net/files/sp-client/sp-webpart-workbench-assembly_en-us_d838220e0a8e0ac1c0d8c6b3cd80d4e2.js:1:749382)
    at Function.e.buildErrorWithVerboseLog (https://spoprod-a.akamaihd.net/files/sp-client/sp-webpart-workbench-assembly_en-us_d838220e0a8e0ac1c0d8c6b3cd80d4e2.js:1:803810)
    at Function.e.buildLoadComponentError (https://spoprod-a.akamaihd.net/files/sp-client/sp-webpart-workbench-assembly_en-us_d838220e0a8e0ac1c0d8c6b3cd80d4e2.js:1:799855)
    at https://spoprod-a.akamaihd.net/files/sp-client/sp-webpart-workbench-assembly_en-us_d838220e0a8e0ac1c0d8c6b3cd80d4e2.js:1:793456

    I should point out jQuery is loading fine when I have something simple in my render method such as:

    SharePoint ♥ jQuery!
    This SPFx webpart is using jQuery ${$.fn.jquery}

    I get the error when I modify the markup to something similar to the form. I’ve rebuilt the form so many times already – any help would be really appreciated!!

    1. Hi,

      Did you add jQuery as global dependency in config.json for spPeoplePicker. Also if yes, then also check that you have any jquery call made in the code. Sometime even if you import jQuery but do not use it explicitly, jQuery does not get loaded and causes issues with dependent libraries.
      Hope it helps

  12. Some users are reporting an error with PeoplePicker field that I’m not able to replicate. As soon as they start typing, they get an alert saying “There was a problem executing your search. Please try again, if the problem persists please refresh the page.” I can see a method called ‘searchFail’ in the sp.peoplepicker.js file but it’s not clear how this is triggered or why.

    Any tips or ideas on how to resolve?

  13. Hi Abhijeet ,

    I developed custom page with people picker it works perfectly when page refresh , but i navigate from HomePage to custom page people picker not working and display below error in console

    Uncaught (in promise) Error: Can not set initial state more than once.
    at f (chunk.vendors~sp-suite-nav-search-common_none_72c39248553682bccc92.js:1)
    at Module.u (chunk.vendors~sp-suite-nav-search-common_none_72c39248553682bccc92.js:1)
    at Module.z (chunk.sp-suite-nav-search-common_none_085e8c791572d4099071.js:1)
    at sp-pages-assembly_en-us_2f138c7077e71d8001064884dc9f6c91.js:1

    this error occurred only when click on left navigation from one site page to another in that case only webpart load not refresh whole page .

    1. Hi Akshay,

      The issue you are facing could be due to the partial reloading feature of Sharepoint in Modern Sites where the pages dont reload completely and your app may not work as desired in case of the partial reloading.
      One of the solutions could be playing with the this.context.application.navigatedEvent.add(this, () => { // update your control }) and seeing if that solves your issue.

      Let us know if you managed to solve this.

  14. Nice Article!

    Is there a way to show email instead of display name inside people picker field?

    Thanks.

  15. Please add this text to the article (makes for easy copypasta)

    “externals”: { “jquery”: { “path”: “https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js”, “globalName”: “jquery”
    }, “bootstrap”: { “path”: “https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js”, “globalName”: “bootstrap”, “globalDependencies”: [“jquery”]
    }, “appjs”: “path”: “lib/webparts/testForm/scripts/app.js”, “globalName”: “appjs”, “globalDependencies”: [“jquery”]
    {
    “sppeoplepicker”: { “path”: “lib/webparts/testForm/scripts/sp.peoplepicker.js”, “globalName”: “sppeoplepicker”, “globalDependencies”: [“jquery”]
    }, “jqueryui”: { “path”: “lib/webparts/testForm/scripts/jquery-ui.js”, “globalName”: “jqueryui”
    }
    1, “localizedResources”: { “TestFormWebPartStrings”: “lib/webparts/testForm/loc/flocalel.js” }

    1. Hi Mark,

      Thanks for the suggestion. I have now updated the post with the details

  16. After debugging the code a bit, it seems that the APPWebURL and hostweburl are coming out to be undefined.
    But replaced them with the current web url , everything worked fine.

    1. Hi Murtuza,

      Yes for some reason in some tenants we have noticed the same and have had to replace with current web url to make them work.

  17. Thank you for posting this article. It has been very helpful. Would you know why I get CORS error when deploying the package to the tenant? It works fine on the workbench but when I do an actual deployment I get ..
    Access to XMLHttpRequest at ‘https://waconatm.officeapps.live.com/apc/trans.gif?79706bb8f463a953a87c437e2281331a’ from origin ‘https://tenant.sharepoint.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

    1. Hi Zuhair,

      Good to know that you found this useful.

      Regarding the CORS messages that you have mentioned, we have noticed those as well in our tenants. Mostly these had no impacts on our solutions and were mostly for some office365 services. For our custom services on Azure we had to enable CORS and allow domains to fix those.
      Hope it helps

  18. Something went wrong
    If the problem persists, contact the site administrator and give them the information in Technical Details.
    TECHNICAL DETAILS
    ERROR:
    jquery__WEBPACK_IMPORTED_MODULE_6__(…).spPeoplePicker is not a function

    CALL STACK:
    TypeError: jquery__WEBPACK_IMPORTED_MODULE_6__(…).spPeoplePicker is not a function
    at SpdrpdownWebPart.xxl9.SpdrpdownWebPart.render (https://localhost:4321/dist/spdrpdown-web-part.js:14073:59)
    at SpdrpdownWebPart.t._renderWithAccessibleTitle (https://modern.akamai.odsp.cdn.office.net/files/sp-client/sp-webpart-workbench-assembly_en-us_6db5d03d6aef1b7d63505cff87b165b2.js:65:832744)
    at https://modern.akamai.odsp.cdn.office.net/files/sp-client/sp-webpart-workbench-assembly_en-us_6db5d03d6aef1b7d63505cff87b165b2.js:65:832298

    1. Hi Santosh,

      Thanks for reaching out. Can you confirm if all the changes mentioned in the config section were done. Also didi you add the jquery global dependency for peoplepicker in the config. Also do check if the sppeoplepicker was added as reference properly in the webpart source

  19. Hi Abhi,

    Yes , i have done it. Please find the below code from my config section

    “externals”: {

    “jquery”: {
    “path”: “https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js”,
    “globalName”: “jquery”
    },
    “bootstrap”: {
    “path”: “https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js”,
    “globalName”: “bootstrap”,
    “globalDependencies”: [“jquery”]
    },
    “appjs”: {
    “path”: “lib/webparts/spdrpdown/scripts/app.js”,
    “globalName”: “appjs”
    },
    “sppeoplepicker”: {
    “path”: “lib/webparts/spdrpdown/scripts/sp.peoplepicker.js”,
    “globalName”: “sppeoplepicker”
    },
    “jqueryui”: {
    “path”: “lib/webparts/spdrpdown/Scripts/jquery-ui.js”,
    “globalName”: “jqueryui”
    }

    },
    “localizedResources”: {
    “SpdrpdownWebPartStrings”: “lib/webparts/spdrpdown/loc/{locale}.js”
    }
    }

    import styles from ‘./SpdrpdownWebPart.module.scss’;
    import * as strings from ‘SpdrpdownWebPartStrings’;
    import { SPComponentLoader } from ‘@microsoft/sp-loader’;
    import pnp, { sp, Item, ItemAddResult, ItemUpdateResult, Web } from ‘sp-pnp-js’;
    import * as $ from ‘jquery’;
    require(‘bootstrap’);
    require(‘./css/jquery-ui.css’);
    let cssURL = “https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css”;
    SPComponentLoader.loadCss(cssURL);
    SPComponentLoader.loadScript(“https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js”);
    require(‘appjs’);
    require(‘sppeoplepicker’);
    require(‘jqueryui’);

  20. Hi Abhi,

    i have to refresh twice to load the control. when i enter to serach in ppl picker n workbench (sharepoint.com/sites/Development/_layouts/15/workbench.aspx)

    VM5952:2 Uncaught Sys.ArgumentNullException: Sys.ArgumentNullException: Value cannot be null.
    Parameter name: serverRelativeUrlOrFullUrl
    at Function.Error.create (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:2736)
    at Function.Error.argumentNull (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:3200)
    at SP.ClientContext.SP.ClientRuntimeContext (:2:54363)
    at Function.Type.initializeBase (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:8478)
    at new SP.ClientContext (:2:25914)
    at Object. (https://localhost:4321/dist/appjs.js:22:46)
    at u (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:27457)
    at Object.fireWith [as resolveWith] (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:28202)
    at k (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:77651)
    at XMLHttpRequest. (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:79907)

  21. Hi Abhijeet,

    i have to refresh twice to load the control. when i enter to serach in ppl picker n workbench (sharepoint.com/sites/Development/_layouts/15/workbench.aspx)

    VM5952:2 Uncaught Sys.ArgumentNullException: Sys.ArgumentNullException: Value cannot be null.
    Parameter name: serverRelativeUrlOrFullUrl
    at Function.Error.create (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:2736)
    at Function.Error.argumentNull (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:3200)
    at SP.ClientContext.SP.ClientRuntimeContext (:2:54363)
    at Function.Type.initializeBase (https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js:5:8478)
    at new SP.ClientContext (:2:25914)
    at Object. (https://localhost:4321/dist/appjs.js:22:46)
    at u (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:27457)
    at Object.fireWith [as resolveWith] (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:28202)
    at k (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:77651)
    at XMLHttpRequest. (https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js:2:79907)

    1. Hi Santosh,

      Thanks for the details.

      Try adding the
      “globalDependencies”: [“jquery”]
      in appjs and peoplepicker and jqueryui as well.

      Also, try to make the following adjustments to the app.js file : TestForm/src/webparts/testForm/scripts/app.js

      Change
      ——-
      var hostUrl = $app.getUrlParamByName(‘SPHostUrl’);
      var appWebUrl = $app.getUrlParamByName(‘SPAppWebUrl’);

      To
      —-
      var hostUrl = ‘Your Sharepoint site collection URL’
      var appWebUrl = ‘Your Sharepoint site collection URL’;

      It seems the URL could not be fetched automatically in your case.

  22. Hi Abhijeet,
    I’m getting the below error message even i followed your previous comments in this blog,

    What might be the issue, could you please assist me on this.

    Something went wrong
    If the problem persists, contact the site administrator and give them the information in Technical Details.
    TECHNICAL DETAILS
    ERROR:
    Cannot read property ‘addEventListener’ of null

    CALL STACK:
    TypeError: Cannot read property ‘addEventListener’ of null
    at SpFxNoJsWebPartWebPart.ZNLJ.SpFxNoJsWebPartWebPart.AddEventListeners (https://localhost:4321/dist/sp-fx-no-js-web-part-web-part.js:8372:48)
    at SpFxNoJsWebPartWebPart.ZNLJ.SpFxNoJsWebPartWebPart.render (https://localhost:4321/dist/sp-fx-no-js-web-part-web-part.js:8365:14)
    at SpFxNoJsWebPartWebPart.t._renderWithAccessibleTitle (https://modern.akamai.odsp.cdn.office.net/files/sp-client/sp-webpart-workbench-assembly_en-us_9b5448a119d43621ffe04aad6c02dfc6.js:65:833935)
    at https://modern.akamai.odsp.cdn.office.net/files/sp-client/sp-webpart-workbench-assembly_en-us_9b5448a119d43621ffe04aad6c02dfc6.js:65:833489

  23. Hello,

    Thank you so much for this post. It was very helpful and educational. However, I am encountering an issue with the peoplepicker. It does not find any users and does not pass any data to SharePoint. When I type in a name, I get a notification box saying that it is “searching” but no items are actually found. Currently I am trying to pass three items into a SharePoint list.

    1. Value from peoplepicker. (user name)
    2. Email (Entered by user)
    3. Date (Entered by user)

    This is how my HTML looks:

    * Requestor Name

    * Email

    * Date Required

    This is at the end of my render function:

    ($(‘#ppDefault’)).spPeoplePicker({
    minSearchTriggerLength: 2,
    maximumEntitySuggestions: 10,
    principalType: 1,
    principalSource: 15,
    searchPrefix: ”,
    searchSuffix: ”,
    displayResultCount: 6,
    maxSelectedUsers: 1
    });

    this._bindEvents();
    }

    The logic responsible for writing the results to SharePoint:

    private _bindEvents(): void {
    this.domElement.querySelector(‘#submit’).addEventListener(‘click’, () => { this.addListItem(); });
    }

    private addListItem(): void {

    var userInfo = ($(‘#ppDefault’)).spPeoplePicker(‘get’);
    var userId;
    var userDetails = this.GetUserId(userInfo[0].email.toString());
    console.log(JSON.stringify(userDetails));
    userId = userDetails.d.Id;

    var email = document.getElementById(“email”)[“value”];
    var date = document.getElementById(“date”)[“value”];

    const siteurl: string = this.context.pageContext.site.absoluteUrl + “/_api/web/lists/getbytitle(‘proof_of_concept_test’)/items”;

    pnp.sp.web.lists.getByTitle(“proof_of_concept_test”).items.add({
    “RequestorName”: userId,
    “Email”: email,
    “DateRequired”: date,
    }).then(r => {
    alert(“success”);
    });
    }

    private GetUserId(userName) {
    var siteUrl = this.context.pageContext.web.absoluteUrl;

    var call = $.ajax({
    url: siteUrl + “/_api/web/siteusers/getbyloginname(@v)?@v=%27i:0%23.f|membership|” + userName + “%27”,
    method: “GET”,
    headers: { “Accept”: “application/json; odata=verbose” },
    async: false,
    dataType: ‘json’
    }).responseJSON;
    return call;
    }