cascade dropdown is not working as expected

87 Views Asked by At

we are working on a cascade webpart dropdown in SPFX, we find an MS article which is the same example what we are looking and expecting.....

but in our case we want to work with SP, SP lists has to load in to first dropdown, then corresponding list views has to load in to the second dropdown.

from the MS article we find two sample functions to load lists into first dropdown and items on to second dropdown. instead we need SP lists and List views need to load.

@Hugo Bernier helped the sample example which is working perfect, but when we updated/replaced with two SP functions(to load lists, and list view to the selected list)

solution is not working as expected, its loading the lists , views are loading in to second dropdown but second dropdown is not working as expected when we select the views

Here is the functions

private async _getSiteLists(): Promise<string[]>{
    const endpoint :string = `${this.context.pageContext.web.absoluteUrl}/_api/web/lists?$select=Title&$filter=(Hidden eq false)&$orderby=Title`;
    const rawResponce: SPHttpClientResponse=await this.context.spHttpClient.get(endpoint, SPHttpClient.configurations.v1);
    return(await rawResponce.json()).value.map(
      (list:{Title:string})=>{
        return list.Title;
      }
    );
  }
private async _getSitelistviews(): Promise<string[]>{
    //let listname:string= this.properties.SelectedList;
    const endpoint :string = this.context.pageContext.web.absoluteUrl+"/_api/web/getlistbytitle('**TestList**')/views?$select=Title";
    console.log("from view api",endpoint)
    console.log(this.properties.SelectedList);
    const rawviewResponce: SPHttpClientResponse=await this.context.spHttpClient.get(endpoint, SPHttpClient.configurations.v1);
    return(await rawviewResponce.json()).value.map(
      (listView:{Title:string})=>{
        return listView.Title;
});}

what I understand is we need to pass the selected value ( from 1 dropdown with selected list) to second function to load the views to that selected list.

here is the actual code webpart .ts file

import {
  IPropertyPaneConfiguration,
  PropertyPaneDropdown,
  IPropertyPaneDropdownOption
} from '@microsoft/sp-property-pane';

export interface IListItemsWebPartProps {
  listName: string;
  itemName: string;
}

export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
  private lists: IPropertyPaneDropdownOption[];
  private items: IPropertyPaneDropdownOption[];
  private listsDropdownDisabled: boolean = true;
  private itemsDropdownDisabled: boolean = true;
  private loadingIndicator: boolean = true;

  public render(): void {
    const element: React.ReactElement<IListItemsProps> = React.createElement(
      ListItems,
      {
        listName: this.properties.listName,
        itemName: this.properties.itemName,
      }
    );

    ReactDom.render(element, this.domElement);
  }
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      showLoadingIndicator: this.loadingIndicator,   
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupFields: [
                PropertyPaneDropdown('listName', {
                  label: strings.ListNameFieldLabel,
                  options: this.lists,
                  disabled: this.listsDropdownDisabled
                }),
                PropertyPaneDropdown('itemName', {
                  label: strings.ItemNameFieldLabel,
                  options: this.items,
                  disabled: this.itemsDropdownDisabled,
                  selectedKey: this.properties.itemName // don't forget to bind this property so it is refreshed when the parent property changes
    })]}]}]};}

protected async onPropertyPaneConfigurationStart(): Promise<void> {
    // disable the item selector until lists have been loaded
    this.listsDropdownDisabled = !this.lists;

    // disable the item selector until items have been loaded or if the list has not been selected
    this.itemsDropdownDisabled = !this.properties.listName || !this.items;

    // nothing to do until someone selects a list
    if (this.lists) {
      return;
    }

    // show a loading indicator in the property pane while loading lists and items
    this.loadingIndicator = true;
    this.context.propertyPane.refresh();

    // load the lists from SharePoint
    const listOptions: IPropertyPaneDropdownOption[] = await this.loadLists();
    this.lists = listOptions;
    this.listsDropdownDisabled = false;

    // load the items from SharePoint
    const itemOptions: IPropertyPaneDropdownOption[] = await this.loadItems();
    this.items = itemOptions;
    this.itemsDropdownDisabled = !this.properties.listName;

    // remove the loading indicator
    this.loadingIndicator = false;
    this.context.propertyPane.refresh();
  }

protected async onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): Promise<void> {
    if (propertyPath === 'listName' && newValue) {
      // communicate loading items
      this.loadingIndicator = true;

      // push new list value
      super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);

      // reset selected item
      this.properties.itemName = ''; // use empty string to force property pane to reset the selected item. undefined will not trigger the reset

      // disable item selector until new items are loaded
      this.itemsDropdownDisabled = true;

      // refresh the item selector control by repainting the property pane
      this.context.propertyPane.refresh();

      // get new items
      const itemOptions: IPropertyPaneDropdownOption[] = await this.loadItems();

      // store items
      this.items = itemOptions;

      // enable item selector
      this.itemsDropdownDisabled = false;

      // clear status indicator
      this.loadingIndicator = false;

      // refresh the item selector control by repainting the property pane
      this.context.propertyPane.refresh();
    }
    else {
      super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
    }
  }


//here is the two functions which we are trying to update to get SP lists.

private async loadLists(): Promise<IPropertyPaneDropdownOption[]> {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return await new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, _reject: (error: any) => void) => {
      setTimeout((): void => {
        resolve([{
          key: 'sharedDocuments',
          text: 'Shared Documents'
        },
        {
          key: 'myDocuments',
          text: 'My Documents'
        }]);
      }, 2000);
    });
  }

private async loadItems(): Promise<IPropertyPaneDropdownOption[]> {
    if (!this.properties.listName) {
      // return empty options since no list has been selected
      return [];
    }

    // This is where you'd replace the mock data with the actual data from SharePoint
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return await new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
      // timeout to simulate async call
      setTimeout(() => {
        const items: { [key: string]: { key: string; text: string }[] } = {
          sharedDocuments: [
            {
              key: 'spfx_presentation.pptx',
              text: 'SPFx for the masses'
            },
            {
              key: 'hello-world.spapp',
              text: 'hello-world.spapp'
            }
          ],
          myDocuments: [
            {
              key: 'isaiah_cv.docx',
              text: 'Isaiah CV'
            },
            {
              key: 'isaiah_expenses.xlsx',
              text: 'Isaiah Expenses'
            }
          ]
        };
        resolve(items[this.properties.listName]);
      }, 2000);
    });
  }
}

Any help please to update in to get lists and list views in to these dropdowns?

2

There are 2 best solutions below

0
Nikolay On

I think the easiest way you use pnpjs library to work with SharePoint (it is a specialized lib that works fine in SPFx as well), instead of working with the built-in client, which is messy.

Assuming you are starting from the unmodified sample you downloaded from GitHub. Step by step:

  1. Install pnpjs:
> npm install @pnp/sp --save
  1. At the top of the ListItemsWebPart.ts add:
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/views";
  1. Replace function loadLists with this code:
  private async loadLists(): Promise<IPropertyPaneDropdownOption[]> {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return await new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, _reject: (error: any) => void) => {

      const sp = spfi().using(SPFx(this.context));

      sp.web.lists.select('Title')().then((lists) => {
        const options: IPropertyPaneDropdownOption[] = lists.map((list) => {
          return {
            key: list.Title,
            text: list.Title
          };
        });
        resolve(options);
      });
    });
  }
  1. Replace function loadItems with this code:
  private async loadItems(): Promise<IPropertyPaneDropdownOption[]> {
    if (!this.properties.listName) {
      // return empty options since no list has been selected
      return [];
    }

    // This is where you'd replace the mock data with the actual data from SharePoint
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return await new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
      // timeout to simulate async call

      const sp = spfi().using(SPFx(this.context));

      sp.web.lists.getByTitle(this.properties.listName).views.select('Title')().then((items) => {
        const options: IPropertyPaneDropdownOption[] = items.map((item) => {
          return {
            key: item.Title,
            text: item.Title
          };
        });
        resolve(options);
      });
    });
  }
1
Esub Vali Sayyed On

@Murali,

please try with below code



private async loadLists(): Promise<IPropertyPaneDropdownOption[]>{

   const endpoint :string = `${this.context.pageContext.web.absoluteUrl}/_api/web/lists?$select=Title,Id &$filter=(Hidden eq false)&$orderby=Title`;

   const rawResponce: SPHttpClientResponse=await this.context.spHttpClient.get(endpoint, SPHttpClient.configurations.v1);

   let finalArray: any[] = [];

   let result= (await rawResponce.json()).value.map((list:{Id: string,Title:string})=>{

    return [...finalArray, {'key': list.Title,'text': list.Title}];

  });

return result.flat(); 

}