EDITED WITH CORRECT ANSWER
So I am customizing an SPFx component's property pane with a PropertyFieldSitePicker
and a PropertyFieldListPicker
. My issue is that the PropertyFieldMultiSelect
lags by one selection when I select options from the PropertyFieldListPicker
menu. However, if I make selections it will then update making me suspect it is a React state issue.
In the code below the following is the code logic
PropertyFieldListPicker('lists', {
// ... more props
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
}),
then
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
if (newValue !== oldValue && propertyPath === 'lists') {
this.loadPropertyPaneResources()
}
}
then
// this gets modified by loadPropertyPaneResources below
private _locationOptions: [];
protected loadPropertyPaneResources(): Promise<void> {
let listUrl = `...`
//... more code
return new Promise((resolve, reject) => {
this.context.spHttpClient.get(listUrl, SPHttpClient.configurations.v1)
.then((response: SPHttpClientResponse) => response.json())
.then((responseJSON: any) => {
this._locationOptions = responseJSON.value.map(item => {
if (item.displayName) {
return { key: item.Id, text: item.displayName }
}
return { key: item.Id, text: item.Title }
})
this.context.propertyPane.refresh()
resolve()
})
// ... more code
});
}
then
PropertyFieldMultiSelect('selectedLocations', {
key: 'selectedLocations',
label: 'Select Office Locations',
options: this._locationOptions, // from loadPropertyPaneResources above
selectedKeys: this.properties.selectedLocations
}),
FULL COMPONENT CLASS
export default class WorldClockWebPart extends BaseClientSideWebPart<IWorldClockProps> {
private _themeProvider: ThemeProvider
private _themeVariant: IReadonlyTheme | undefined
private _locationOptions: []
private _handleThemeChangedEvent(args: ThemeChangedEventArgs): void {
this._themeVariant = args.theme
this.render()
}
protected loadPropertyPaneResources(): Promise<void> {
let listUrl = `${this.context.pageContext.web.absoluteUrl}/_api/lists/GetByTitle('World Clock Locations')/items`;
const { sites, lists } = this.properties
if (sites && sites[0] && sites[0].url && lists) {
listUrl = `${sites[0].url}/_api/lists(guid'${lists}')/items`;
}
return new Promise((resolve, reject) => {
this.context.spHttpClient.get(listUrl, SPHttpClient.configurations.v1)
.then((response: SPHttpClientResponse) => response.json())
.then((responseJSON: any) => {
this._locationOptions = responseJSON.value.map(item => {
if (item.displayName) {
return { key: item.Id, text: item.displayName }
}
return { key: item.Id, text: item.Title }
});
resolve();
})
.catch(error => {
console.error(error);
resolve();
});
});
}
protected onInit(): Promise<void> {
// Consume the new ThemeProvider service
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey)
// If it exists, get the theme variant
this._themeVariant = this._themeProvider.tryGetTheme()
// Register a handler to be notified if the theme variant changes
this._themeProvider.themeChangedEvent.add(this, this._handleThemeChangedEvent)
return super.onInit()
}
public render(): void {
const element: React.ReactElement<IWorldClockProps> = React.createElement(
WorldClock,
{
title: this.properties.title,
context: this.context,
displayMode: this.displayMode,
titlePlaceholder: this.properties.titlePlaceholder,
themeVariant: this._themeVariant,
updateProperty: (value: string) => {
this.properties.title = value
},
locations: [],
seeMoreUrl: this.properties.seeMoreUrl,
selectedLocations: this.properties.selectedLocations,
sites: this.properties.sites,
lists: this.properties.lists, // Stores the list ID(s)
}
)
ReactDom.render(element, this.domElement)
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement)
}
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
if (newValue !== oldValue && propertyPath === 'lists') {
this.loadPropertyPaneResources()
}
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription,
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('seeMoreUrl', {
label: 'See More URL',
}),
PropertyFieldMultiSelect('selectedLocations', {
key: 'selectedLocations',
label: 'Select Office Locations',
options: this._locationOptions,
selectedKeys: this.properties.selectedLocations
}),
PropertyFieldSitePicker('sites', {
label: 'Select sites',
initialSites: this.properties.sites,
context: this.context,
deferredValidationTime: 500,
multiSelect: false,
onPropertyChange: this.onPropertyPaneFieldChanged,
properties: this.properties,
key: 'sites'
}),
PropertyFieldListPicker('lists', {
label: 'Select a list',
selectedList: this.properties.lists,
includeHidden: false,
orderBy: PropertyFieldListPickerOrderBy.Title,
disabled: false,
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
properties: this.properties,
context: this.context,
onGetErrorMessage: null,
deferredValidationTime: 0,
key: 'listPickerFieldId',
webAbsoluteUrl: this.properties.sites[0].url
}),
],
},
],
},
],
}
}
protected get dataVersion(): Version {
return Version.parse('1.0')
}
}
References: https://pnp.github.io/sp-dev-fx-property-controls/controls/PropertyFieldListPicker/ https://pnp.github.io/sp-dev-fx-property-controls/controls/PropertyFieldMultiSelect/ https://pnp.github.io/sp-dev-fx-property-controls/controls/PropertyFieldSitePicker/
I needed to call
this.context.propertyPane.refresh()
before I resolve the promise inloadPropertyPaneResources
.