Preface
I recently started working on SpFX framework for SharePoint client side development. I was going through SpFx Pnp samples. While samples are great, online documentation is not. It is verbose yet cryptic. I thought I'd break the samples down for my own learning & write these blogs to simplify for anyone else who picks up these samples to get his/her feet wet with SpFX. There might be a series of posts in coming days if don't lose interest or don't find anything better to do 😃
SpFx samples are available here: https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples .
We'd look into react related samples.
PropertyPaneAsyncPaneDropdown
is there in PnP samples as an example of asynchronously populated "select
" control in property pane. There are 4 typeScript interfaces in play and we should discuss them first.
IPropertyPaneAsyncDropdownProps
First one up is IPropertyPaneAsyncDropdownProps
. The purpose of this interface is to pass information from webpart file to the custom control, i.e., PropertyPaneAsyncDropdown
. Interface looks like:
export interface IPropertyPaneAsyncDropdownProps
{
label : string ;
loadOptions : () => Promise < IDropdownOption []>;
onPropertyChange : ( propertyPath : string , newValue : any ) => void ;
selectedKey : string | number ;
disabled ?: boolean ;
}
label
: Label of the property in property pane. loadOptions
: Method to fetch options for the dropdown, in our case method to fetch list information from Sharepoint so that user can select a list name in property pane. onPropertyChange
: Method to execute when user selects an item in the dropdown, i.e., when user selects a list. selectedKey
: Value from the selected option in dropdown. In our case, it is the list Url of selected list. disabled
: We are not using it but could be used to render the control disabled.
IPropertyPaneAsyncDropdownInternalProps
Second up is IPropertyPaneAsyncDropdownInternalProps
. This interface extends IPropertyPaneAsyncDropdownProps
(our own) and IPropertyPaneCustomFieldProps
(imported from @microsoft/sp-webpart-base
) and it doesn't implement any attribute of its own. We need an instance of IPropertyPaneCustomFieldProps
to work with a custom property control & IPropertyPaneAsyncDropdownInternalProps
is an instance of one. Besides the attributes we defined in our IPropertyPaneAsyncDropdownProps
, we'd be using onRender
attribute (inherited from IPropertyPaneCustomFieldProps
).
key
is another mandatory attribute from IPropertyPaneCustomFieldProps
& we'll set it.
Before we discuss the remaining two interfaces, let's dive into the custom property itself.
PropertyPaneAsyncDropdown
This class implements IPropertyPaneField<IPropertyPaneAsyncDropdownProps>
, which dictates that our class should have these three attributes (which we do):
public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;
public targetProperty: string;
public properties: IPropertyPaneAsyncDropdownInternalProps;
Well, as per the dictation of implemented interface, properties attribute should have type IPropertyPaneAsyncDropdownProps
, but remember IPropertyPaneAsyncDropdownInternalProps extends IPropertyPaneAsyncDropdownProps
so we are OK here.
Now, look at the constructor:
constructor ( targetProperty : string , properties : IPropertyPaneAsyncDropdownProps )
{
this . targetProperty = targetProperty ;
this . properties =
{
key : properties . label ,
label : properties . label ,
loadOptions : properties . loadOptions ,
onPropertyChange : properties . onPropertyChange ,
selectedKey : properties . selectedKey ,
disabled : properties . disabled ,
onRender : this . onRender . bind ( this )
};
}
Remember, we create the instance of this class from webpart itself so we pass the property name from there, also we pass an instance of IPropertyPaneAsyncDropdownProps
from webpart code. key
and onRender
are two attributes which IPropertyPaneAsyncDropdownInternalProps
inherits from IPropertyPaneCustomFieldProps
and we set key
to label
and onRender
to a locally defined function.
IAsyncDropdownProps
We will discuss this interface in context to where it is used in PropertyPaneAsyncDropdown
class in onRender
method.
private onRender ( elem : HTMLElement ): void
{
if (! this . elem )
{
this . elem = elem ;
}
const element : React . ReactElement < IAsyncDropdownProps > =
React . createElement ( AsyncDropdown ,
{
label: this . properties . label ,
loadOptions: this . properties . loadOptions ,
onChanged: this . onChanged . bind ( this ),
selectedKey: this . properties . selectedKey ,
disabled: this . properties . disabled ,
stateKey: new Date (). toString ()
});
ReactDom . render ( element , elem );
}
stateKey
& onChanged
are worth discussing. We set stateKey
to current time so that every time user opens the property pane, stateKey
has a new value. We'd use this fact to reload the select
options every time user opens the property pane.
onChanged
is set to a locally defined function as:
private onChanged(option: IDropdownOption, index?: number): void {
this.properties.onPropertyChange(this.targetProperty, option.key);
}
All this function does is it calls the method passed by the web part.
ListViewWebPart
This is the webpart file itself. We should look the property setup here.
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration
{
return
{
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
new PropertyPaneAsyncDropdown('listUrl', {
label: strings.ListFieldLabel,
loadOptions: this.loadLists.bind(this),
onPropertyChange: this.onListChange.bind(this),
selectedKey: this.properties.listUrl
})
]
}
]
}
]
};
}
We should look at the onPropertyChange
function here:
private onListChange(propertyPath: string, newValue: any): void
{
const oldValue: any = get(this.properties, propertyPath);
if (oldValue !== newValue)
{
}
update( this.properties, propertyPath, (): any => newValue );
this.context.propertyPane.refresh();
this.render();
}
Whenever the user selects a new value in the dropdown, we need to update the webpart property explicitly via "update
" call (imported from @microsoft/sp-lodash-subset
).
There is one more file "AsyncDropdown.tsx" which plays an important role but this file, while it's long, is also self explanatory.
Here is a link to the src folder for this webpart.
My next step would be to introduce a dependent cascaded drop down control (to select list columns). Eventually, I'd render a list view of selected columns.
CodeProject