Creating A Personalized Per User SPFx WebPart Property

Custom webpart properties provide a great way to support configurable values for SPFx webparts. The properties which we configure using the default property pane of an SPFx webpart can be changed when one edits the page and these property values are then for all users.
 
The traditional webparts which were used in the classic pages, supported User Scoped or Shared custom properties. The custom properties in SPFx webparts are always scoped to be shared, and they do not support User scoped properties. User scoped properties can be really useful for some use cases where we want to allow the end users to change the configurations or properties.
 
For example, a world time webpart can allow each user to choose their own choice of a time zone and shows a clock with that time zone.
Watch this video for detailed steps on how we can extend the world time webpart to support personalization
 

How to support User Scoped properties in SPFx webparts

 
As SPFx webparts doesn’t support user scoped properties by default, the same can be handled using custom code. Here is a high level overview of what we need
  1. A unique id to identify the webpart instance
  2. The logged in user’s name
  3. A custom Panel to allow the user to change the values
  4. Storing the user selected value/s for the properties.
Below are the steps on how to extend the worldTime webpart from PnP SP Starter Kit to allow end users to choose a time zone of their own.
 

A unique id to identify the webpart instance

 
We have to save the user selected property values for a specific webpart instance. This should be unique so that we know what user selected value should be used later for a specific webpart.
 
The WebPartContext class provides a property called instanceId which we can use to uniquely identify an instance of a webpart added to a page. This value can be passed to the component which needs the user selected value.
 

The logged in user’s name

 
We would need the logged in user’s name to save the user selected values for the webpart. Use the PageContext object to get the current user’s login name
 
In the World Time webpart sample make the following changes to get the webpart unique Id and logged in user’s name
  1. export interface IWorldTimeProps {  
  2.   description: string;  
  3.   timeZoneOffset: number;  
  4.   errorHandler: (errorMessage: string) => void;  
  5.   webpartId: string;  
  6.   loginName: string;  

Pass this value to the React component
  1. const element: React.ReactElement<IWorldTimeProps> = React.createElement(  
  2.   WorldTime,  
  3.   {  
  4.     description: (timeZones.TimeZones.getTimeZone(this.properties.timeZoneOffset)).displayName,  
  5.     timeZoneOffset: this.properties.timeZoneOffset,  
  6.     errorHandler: (errorMessage: string) => {  
  7.       this.context.statusRenderer.renderError(this.domElement, errorMessage);  
  8.     },  
  9.     webpartId: this.context.instanceId,  
  10.     loginName: this.context.pageContext.user.loginName  
  11.   }  
  12. ); 

A custom Panel to allow the user to change the values

 
A custom panel has to be shown which allows the end users to change the values. In a world clock example, let's assume we want the end users to select a time zone of their choice. For this we have to show a dropdown with the list of time zones using a Fluent UI Panel.
 
Here is a snippet which is added to the WorldTime component in our example
  1. <Panel isOpen={this.state.showPanel}>  
  2.     Change the timezone  
  3.     <Dropdown options={this.getTimeZones()} selectedKey={timeZoneOffset} onChange={this._onPropertyChanged}></Dropdown>  
  4. </Panel> 

Storing the user selected value/s for the properties.

 
Once the end users change the selected value, this has to be stored and used for future page loads. This value should be saved for the current user and for the specific webpart instance id. There could be multiple options on which to save this data.
  • Browser Storage
    This is easy to implement to save the data on the browser's local storage. However this will not work when the user opens the page from a different browser or device.

  • Persisted storage
    Like a SharePoint list, User’s onedrive or any other storage system. This will be more robust as the value is persisted and will work when opened from any browser and device.
A sample code on how to use the PnP Client Storage to save these values locally.
  1. private _storage:PnPClientStorage;  
  2. ...  
  3. this._storage = new PnPClientStorage();  
  4. ...  
  5. // To get the value stored in local storage  
  6. this._storage.local.get(`${this.props.webpartId}-${this.props.loginName}`) as number;  
  7.   
  8. //To save the value in local storage  
  9. private _onPropertyChanged = (event, option, index) => {  
  10. this.setState({  
  11.     timeZoneOffset:option.key,  
  12.     showPanel: false  
  13. }, () => {  
  14.     this._storage.local.put(`${this.props.webpartId}-${this.props.loginName}`, option.key);  
  15. });  

The complete code sample can be found in the Github Repo
 
This blog was originally posted here