Auto populate user & manager (using PNPJS + Office UI Fabric Persona + Graph client)

In most of the user request forms users wish to have some of the users details to be auto populated instead of entering manually to avoid human error and to maintain consistency. Especially the manger details, needs to be dynamically loaded from the User Profile.

In this article we will see how to auto populate current user in a people picker and their manger details in a Persona control. We will also see how to update manager details when user is changed in people picker. Thanks for Waldek for sp-starter-kit. It was so helpful.

What is used?

  • PNP SPFX People picker reusable control
  • Office UI Fabric Persona control
  • PNPJS to get user profile details
  • Graph API used to get user picture

Step 1: Create SPFX React webpart

Step 2: Add a People picker

I have used the PnP SPFX reusable react controls people picker. Install it with the below command into the project terminal

npm install @pnp/spfx-controls-react --save --save-exact

Open “.tsx” file and add this import statement just below the default imports

import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";

Add the below people picker now into the method return statement

<PeoplePicker
            context={this.props.context}
            titleText="Requestor Name"
            personSelectionLimit={1}
            groupName={"Accounts Owners"} // Leave this blank in case you want to filter from all users
            showtooltip={true}
            isRequired={true}
            //disabled={true}
            selectedItems={this._getPeoplePickerItems}            
            showHiddenInUI={false}
            principalTypes={[PrincipalType.User]}
            resolveDelay={1000} 
            defaultSelectedUsers={this.state.curUserTitle} />

Step 3: Add Persona

To display the Manager photo and details I am going to use an Office UI Fabric Persona. You can still use another people picker and disable it to make it non editable. But I just want to try 😊 with Persona.

npm install office-ui-fabric-react

Add the below import statement just below the people picker import statement

import { Persona, PersonaSize, PersonaPresence } from 'office-ui-fabric-react/lib/Persona';
import { Stack } from 'office-ui-fabric-react/lib/Stack';

Add the below persona into the render method return statement

 <div>
        <p>Requestor Manager Name</p>
        <Stack tokens={{ childrenGap: 10 }}>
        <Persona imageUrl={this.state.image} text={this.state.managerName} secondaryText={this.state.mgrText} showSecondaryText={true} size={PersonaSize.size28} presence={PersonaPresence.none} />
      </Stack>
      </div>

Step 4: Declare State and Props

Create the below interface in the .tsx file just above the default class

export interface ISpfxpeoplepickerState{ 
  managerName: string;
  curUserLoginName: string;
  curUserTitle: string[];
  mgrimageUrl: string;
  mgrText: string;
  image: string;  
}

Open “ISpfxpeoplepickerProps.ts” and replace it with below code

import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphClient } from '@microsoft/sp-http';
export interface ISpfxpeoplepickerProps {
  description: string;
  context: WebPartContext;
  graphClient: MSGraphClient;
}

We are ready now to load the data to the added controls.

Step 4: Load current user people picker

To get current user logged in I am going to use PnP SP JS and load it in people picker

npm install @pnp/sp

Add these import statement in .tsx file

import { sp } from "@pnp/sp";
import { CurrentUser } from '@pnp/sp/src/siteusers';

As we need to load current user on webpart load we will add code inside componentDidMount() method

public componentDidMount():void{
    sp.web.currentUser.get().then((r: CurrentUser) => {
      this.setState({ curUserLoginName: r['LoginName'], curUserTitle: [r['Title']]});  
      this._getManagerName(this.state.curUserLoginName);
    });
  } 

Add these private methods inside the class to get and load manager details

//This will get ManagerName only by sending current user name
  private _getManagerName(mgrUserName:string){
    sp.profiles.getPropertiesFor(mgrUserName).then((profile: any) => {
    var properties = {};
    profile.UserProfileProperties.forEach(function(prop) {
    properties[prop.Key] = prop.Value;
    });
    profile.userProperties = properties;
    this._getManagerDetails(properties["Manager"]);
    });
  }

  //To get more details about manager by sending manager name
  private _getManagerDetails(user:string){
    sp.profiles.getPropertiesFor(user).then((profile: any) => {
    var properties = {};
    profile.UserProfileProperties.forEach(function(prop) {
    properties[prop.Key] = prop.Value;
    });
    profile.userProperties = properties;
    var userName:string = properties['UserName'];
    this.setState({ managerName: properties["PreferredName"], mgrText: properties["SPS-JobTitle"]});
    this._getMgrPhoto(userName);    
  });
  }
Step 5: Load manager picture using GraphClient

Open the .ts webpart file. Add this import statement

import { MSGraphClient } from '@microsoft/sp-http';
import { sp } from "@pnp/sp";

then add this onInit() method inside default class

private graphClient: MSGraphClient;
  public onInit(): Promise<void> {
    return new Promise<void>((resolve: () => void, reject: (error: any) => void): void => {
      sp.setup({
        spfxContext: this.context
        });
      this.context.msGraphClientFactory
        .getClient()
        .then((client: MSGraphClient): void => {
          this.graphClient = client;
          resolve();
        }, err => reject(err));
    });    
  }

public render(): void {
    const element: React.ReactElement<ISpfxpeoplepickerProps > = React.createElement(
      Spfxpeoplepicker,
      {
        description: this.properties.description,
        context:this.context,
        graphClient: this.graphClient,
      }
    );

    ReactDom.render(element, this.domElement);
  }
Why do we need to use Graph for this?

I tried using “sp.profiles.profileproperties” but for “PictureURL” is empty that’s because users profile picture is stored as “blob” which can be retrieved using Graph client. Graph Explorer is really amazing 😊

Open the .tsx file and add this private method inside the class

//To get manager picture from graph api
  private _getMgrPhoto(mgrname:string){
    console.log("ManagerName : "+mgrname);
    this.props.graphClient
    .api(`/users/${mgrname}/photo/$value`)
    .responseType('blob')
    .get((err: any, photoResponse: any, rawResponse: any) => {
      const blobUrl = window.URL.createObjectURL(photoResponse);
      this.setState({ image: blobUrl });
    });           
  }

As our webpart need to connect to Graph api we need to add this “webApiPermissionRequests” in “package-solution.json” file

"webApiPermissionRequests": [{  
      "resource": "Microsoft Graph",  
      "scope": "User.ReadBasic.All"  
      }]
Step 6: Add people picker on change event
private _getPeoplePickerItems = (items: any[]):void => {
  if(items && items.length){
  sp.profiles.getPropertiesFor(items[0]['loginName']).then((profile: any) => {
    //console.log(profile);
    var properties = {};
    profile.UserProfileProperties.forEach(function(prop) {
    properties[prop.Key] = prop.Value;
    });
    profile.userProperties = properties; 
    this._getManagerDetails(properties["Manager"]);  
    });
  }
  }
Step 7: Deploy
gulp build
gulp bundle --ship
gulp package-solution –ship

Get the spfx package file from “sharepoint\solution” and upload it to your app catalogue site

Once deployed go to SharePoint Admin site –> API Management, and approve it.

We are ready to add our webpart into a SharePoint page. Really superb!!!…

My webpart loads like charm. It loads current user logged and the user’s manager. It also allows me to change the user in the people picker which relatively updates the manage details also in the persona.