Articles

Create multi select dropdown with custom load options in SPFX

Create multi select dropdown with custom load options in SPFX


In this article we will create a dropdown control with custom load options in the property pane of SharePoint Framework or spfx web part.Custom property pane controls implement IPropertyPaneField<Tproperties> interface where Tproperties is an interface which extends IPropertyPaneCustomFieldProps interface.

Lets see step by step guide of how to create a multiselect dropdown with custom load options.
 
  1. Create a SharePoint Framework or SPFX web part
  2. Create a  react component
  3. Create an interface to define properties of mutiselect property pane control
  4. Create multiselect drop down property pane control
  5. Use multiselect dropdown in web part properties

1. Create a SharePoint Framework or SPFX web part

Open a command prompt , navigate to the directory where you want to create web part and type 
mkdir multiselectDropdownDemo
to create a directory for your solution and then navigate to the directory by typing 
cd multiselectDropdownDemo
Now create your web part by typing below command in your command prompt
Yo @microsoft/sharepoint and answer the questions as below.
What is your web part name : multiselectDropdownDemo
Which base line package you want to target your web part :choose SharePoint Online Only (Latest)
Where do you want to place files : choose current folder
Do you want to allow tenant admin the choice of being able to deploy the solution to all sites immediately .. : Answer y
Which type of client side component to create : WebPart
What is your web part name ? : multiselectDropdownDemo
And then press Enter and choose React option when asked for framework options.
After scaffolding completed open the web part by typing code . in the command prompt.
So we have created our web part.
 

2. Create a React Component

In this step we will create a react component  which will be used as multislect select dropdown.
 
Create a folder named controls in src/webparts folder and then inside controls create a folder named components.
Lets start creating our component.
In the components folder create a file named IListDropdownProps.ts and add below code there.
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';    
    
export interface IListDropdownProps {    
  label: string;    
  loadOptions: () =&gt; Promise&lt;IDropdownOption[]&gt;;    
  onChanged: (option: IDropdownOption, index?: number) =&gt; void;    
  selectedKeys: string[] | number[];  
  multiselect:boolean;
  disabled: boolean;    
  stateKey: string;    
} 
Here label is the label of dropdown ,
loadOptions is method which can be called to populate the dropdown ,
onChanged will be called when state of dropdown will be changed.
selectedKeys are the keys of items selected in dropdown,
mutiselect is a Boolean property which will define if dropdown will be multiselect.
Disabled property will define if the Dropdown needs to be disabled.
Create another file named IListDropdownState.ts to define the type that will be used in state of our component as paste below code
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';    
    
export interface IListDropdownState {    
  loading: boolean;    
  options: IDropdownOption[];    
  error: string;  
  selectedKeys: string[] | number[]; 
}  
At any time this will represent the state of dropdown.
Now create another file named ListDropdown.tsx as paste the code below.
import * as React from 'react';  
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';  
import { Spinner } from 'office-ui-fabric-react/lib/components/Spinner';  
import { IListDropdownProps } from './IListDropdownProps';  
import { IListDropdownState } from './IListDropdownState';  
  
export default class ListDropdown extends React.Component&lt;IListDropdownProps, IListDropdownState&gt; {  
  
  
  constructor(props: IListDropdownProps, state: IListDropdownState) {  
    super(props);  
    this.state = {  
      loading: false,  
      options: undefined, 
      selectedKeys :this.props.selectedKeys, 
      error: undefined  
    };  
  }  
  
  public componentDidMount(): void {  
    this.loadOptions();  
  }  
  
  public componentDidUpdate(prevProps: IListDropdownProps, prevState: IListDropdownState): void {  
    if (this.props.disabled !== prevProps.disabled ||  
      this.props.stateKey !== prevProps.stateKey) {  
      this.loadOptions();  
    }  
  }  
  
  private loadOptions(): void {  
    this.setState({  
      loading: true,  
      error: undefined,  
      options: undefined ,
      selectedKeys :this.props.selectedKeys, 
    });  
  
    this.props.loadOptions()  
      .then((options: IDropdownOption[]): void =&gt; {  
        this.setState({  
          loading: false,  
          error: undefined,  
          options: options,
         selectedKeys:this.props.selectedKeys, 
        });  
      }, (error: any): void =&gt; {  
        this.setState((prevState: IListDropdownState, props: IListDropdownProps): IListDropdownState =&gt; {  
          prevState.loading = false;  
          prevState.error = error;  
          return prevState;  
        });  
      });  
  }  
  
  public render(): JSX.Element {  
    const loading: JSX.Element = this.state.loading ? &lt;div&gt;&lt;Spinner label={'Loading options...'} /&gt;&lt;/div&gt; : &lt;div /&gt;;  
    const error: JSX.Element = this.state.error !== undefined ? &lt;div className={'ms-TextField-errorMessage ms-u-slideDownIn20'}&gt;Error while loading items: {this.state.error}&lt;/div&gt; : &lt;div /&gt;;  
   
    return (  
      &lt;div&gt;  
        &lt;Dropdown label={this.props.label}  
          disabled={this.props.disabled || this.state.loading || this.state.error !== undefined}  
          onChanged={this.onChanged.bind(this)}  
          options={this.state.options} 
          selectedKeys={this.state.selectedKeys}
          placeHolder="Select option" multiSelect={this.props.multiselect}/&gt;  
        {loading}  
        {error}  
      &lt;/div&gt;  
    );  
  }  
  
  private onChanged(option: IDropdownOption, index?: number): void {  
      let newselectedkeys:any[]=[...this.state.selectedKeys];
      if (option.selected) {
        newselectedkeys.push(option.key as string);
      } else {
        // Remove the item from the selected keys list
        const itemIdx = newselectedkeys.indexOf(option.key as string);
        if (itemIdx &gt; -1) {
          newselectedkeys.splice(itemIdx, 1);
        }
      }
    this.setState((prevState: IListDropdownState, props: IListDropdownProps): IListDropdownState =&gt; {  
      prevState.selectedKeys=newselectedkeys;
      return prevState;  
    });  
    if (this.props.onChanged) {  
      this.props.onChanged(option, index);  
    }  
  }  
}  

Here onchange method makes sure that if you unselect an item in dropdown then that key is deleted from selectedKeys other wise key is added  and also it calls onChanged method supplied from props to notify that dropdown has been changed.

3. Create an interface to define properties of mutiselect property pane control

In the previous sections we have created a React Component which can be used in multiselect dropdown control. Now lets create an interface to define properties from multiselectDropdownControl.
Create a file named IPropertyPaneMultiselectDropdownProps.ts in controls folder and paste below code.
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';  
  
export interface IPropertyPaneMultiselectDropdownProps
 {  
  label: string;  
  loadOptions: () =&gt; Promise&lt;IDropdownOption[]&gt;;  
  onPropertyChange: (propertyPath: string, newValue: any) =&gt; void;  
  selectedKeys: string[] | number[];  
  disabled?: boolean;  
  multiselect:boolean;

} 
Now we will create another interface to wrap this interface and IpropertyPaneCustomFieldProps.
Create a file under controls folder named
IPropertyPaneMultiselectDropdownInternalProps.ts
and paste below code.
import { IPropertyPaneCustomFieldProps } from '@microsoft/sp-webpart-base';  
import { IPropertyPaneMultiselectDropdownProps } from './IPropertyPaneMultiselectDropdownProps';  
  
export interface IPropertyPaneMultiselectDropdownInternalProps extends IPropertyPaneMultiselectDropdownProps, IPropertyPaneCustomFieldProps {  
}  

4. Create multiselect drop down property pane control

Create a file named “PropertyPaneMultiselectDropdown” in controls folder and paste the code below

import * as React from 'react';  
import * as ReactDom from 'react-dom';  
import {  
  IPropertyPaneField,  
  PropertyPaneFieldType  
} from '@microsoft/sp-webpart-base';  
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';  
import { IPropertyPaneMultiselectDropdownProps } from './IPropertyPaneMultiselectDropdownProps';  
import { IPropertyPaneMultiselectDropdownInternalProps } from './IPropertyPaneMultiselectDropdownInternalProps';  
import ListDropdown from './components/ListDropdown';  
import { IListDropdownProps } from './components/IListDropdownProps';  
import { cloneDeep } from 'lodash';
//multiselect dropdown with custom load deligate and on change handler
export class PropertyPaneMultiselectDropdown implements IPropertyPaneField&lt;IPropertyPaneMultiselectDropdownInternalProps&gt; {  
  public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;  
  public targetProperty: string;  
  public properties: IPropertyPaneMultiselectDropdownInternalProps;  
  private elem: HTMLElement;  
  
  constructor(targetProperty: string, properties: IPropertyPaneMultiselectDropdownProps) {  
    this.targetProperty = targetProperty;  
    this.properties = {  
      key: properties.label,  
      label: properties.label,  
      loadOptions: properties.loadOptions,  
      onPropertyChange: properties.onPropertyChange,  
      selectedKeys: properties.selectedKeys,  
      disabled: properties.disabled, 
      multiselect:properties.multiselect, 
      onRender: this.onRender.bind(this)  
    };  
  }  
  
  public render(): void {  
    if (!this.elem) {  
      return;  
    }  
  
    this.onRender(this.elem);  
  }  
  
  private onRender(elem: HTMLElement): void {  
    if (!this.elem) {  
      this.elem = elem;  
    }  
  
    const element: React.ReactElement&lt;IListDropdownProps&gt; = React.createElement(ListDropdown, {  
      label: this.properties.label,  
      loadOptions: this.properties.loadOptions,  
      onChanged: this.onChanged.bind(this),  
      selectedKeys: this.properties.selectedKeys, 
    
      disabled: this.properties.disabled, 
      multiselect:this.properties.multiselect, 
      // required to allow the component to be re-rendered by calling this.render() externally  
      stateKey: new Date().toString()  
    });  
    ReactDom.render(element, elem);  
  }  
  
  private onChanged(option: IDropdownOption, index?: number): void {  
    const updateSelectedKeys: any[] = this.properties.selectedKeys ? cloneDeep(this.properties.selectedKeys) : [];
  if(this.properties.onPropertyChange)
  {  // Check if item got selected
      if (option.selected) {
        updateSelectedKeys.push(option.key);
      } else {
        // Remove the item from the selected keys list
        const itemIdx =updateSelectedKeys.indexOf(option.key);
        if (itemIdx &gt; -1) {
          updateSelectedKeys.splice(itemIdx, 1);
        }
      }
    }
    this.properties.selectedKeys=updateSelectedKeys;
    this.properties.onPropertyChange(this.targetProperty, updateSelectedKeys);  
  }  
}  

This is actual dropdown property pane control we have created. save the changes and now its time to use the control that we have created.

5. How to use multiselect dropdown in web part properties

To use in web part first we need to import the package as below.

import {PropertyPaneMultiselectDropdown} from '../controls/PropertyPaneMultiselectDropdown';

Then we need to create a property in webpart properties interface.

multiselectvalue:string[];

Then you need to define on property change load methods.

private loadTestData():Promise&lt;IDropdownOption[]&gt;{
      return new Promise&lt;IDropdownOption[]&gt;((resolve)=&gt;{
        resolve([
          {key:'1',text:'test option 1'},
          {key:'2',text:'test option 2'},
          {key:'3',text:'test option 3'},
          {key:'4',text:'test option 4'},

        ]);
      });
    }
    private onPropertyChange(propertyPath: string, newValue: any): void {  
      const oldValue: any = get(this.properties, propertyPath);  
      // store new value in web part properties  
      update(this.properties, propertyPath, (): any =&gt; { return newValue; });  
      // refresh web part  
      this.render();  
    }

And finally use below code in the property to create property pane control.

new PropertyPaneMultiselectDropdown('multiselectvalue',{
                  label:'Multiselect dropdown',
                  loadOptions: this.loadTestData.bind(this),
                  multiselect:true,
                  onPropertyChange:this.onPropertyChange.bind(this),
                  selectedKeys:this.properties.multiselectvalue
                })
 

 

To view How it looks like and to download whole code and working solution you can go to github 

https://github.com/mukeshhope/spfxcomponents

 

 
 
 

SharePoint Online
SharePoint Framework

Would you like to see your article here on tutorialsinhand. Join Write4Us program by tutorialsinhand.com

About the Author
Mukesh Tiwari
Always ready to learn new technologies, A problem solver, exhibit to do attitude. Having around 10+ years of experience of development and management skills. I believe don't just set random goals but right goals suited for you.
Page Views :    Published Date : Jun 27,2020  
Please Share this page

Related Articles

Like every other website we use cookies. By using our site you acknowledge that you have read and understand our Cookie Policy, Privacy Policy, and our Terms of Service. Learn more Got it!