Read all lists/views/fields using pnpjs in spfx webpart

Recently I started exploring pnp js with spfx webparts. I wanted to explore few things which are more of basic functionality for any webparts. In this Post I'll explain
  • How to read all the lists from a site and bind to dropdown in spfx webpart
  • Read all the views for a selected list and bind to 2nd dropdown.
  • Get all the fields/columns for a selected view and render then as checkboxes in spfx webpart.
I have created a react spfx project and imported all the sp,web from @pnp/sp library.

import {sp, Web, List} from '@pnp/sp';

First step is to declare 3 collections for above 3 requirements so that you can use them in getPropertyPageConfiguration() method.


private listDropDownOptions: IPropertyPaneDropdownOption[];
private viewDropDownOptions: IPropertyPaneDropdownOption[];
private fieldsListCollection = [];  

Use these collections in getPropertyPageConfiguration() method.

protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              /**
               * Instead of calling group fields here, we will call method which will return back preoperties to render.
               */
              groupFields: [
                PropertyPaneTextField('description', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            },
            {
              groupName:strings.ListInformationGroup,
              groupFields:[
                PropertyPaneDropdown('listDropdown',{
                  label:strings.ListTitle,
                  options:this.listDropDownOptions
                }),
                PropertyPaneDropdown("viewDropdown",{
                  label:strings.SelectedView,
                  options:this.viewDropDownOptions
                })
              ],
             
            },
            {
              groupName: "Fields",
              groupFields:this.fieldsListCollection
            }
          ]
        }
      ]
    };

Next step is to setup the context

protected onInit() : Promise<void>{
    this.listDropDownOptions = [];
    this.viewDropDownOptions = [];
    return super.onInit().then(_ => {
     
      sp.setup({
        spfxContext : this.context
      });
    });
  }

Now, using pnpjs read all the lists from the web and add them to listDropdown collection. Here is how you can do it. You might need to filter the hidden lists as they are not required.

//get all the lists from the site and populate the dropdown
  private GetLists():Promise<any>{
    return sp.web.lists.filter('Hidden eq false').get().then((data) =>{
      console.log("Total number of lists are " + data.length);
      return data;
    });

Once you have returned all the lists from web, you need to add this to the listDropDownOptions collection which we have bound to the lists dropdown. This is how it can be done.

protected onPropertyPaneConfigurationStart():void{
    this.listsDropdownDisabled = !this.listDropDownOptions;
    this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'lists');
    // loads list name into list dropdown 
    this.GetLists().then((response) =>{
      for(let i=0 ; i< response.length;i++){
        //now populate the listdropdown array
        this.listDropDownOptions.push({key:response[i].Title,text:response[i].Title});
      }
      this.listsDropdownDisabled = false;
      this.context.propertyPane.refresh();
      this.context.statusRenderer.clearLoadingIndicator(this.domElement);
      this.render();
    });
  }

Now, if you run your webpart you will have all the lists from the web rendered in the Lists dropdown.
Next step is to get all the views for the selected list from the dropdown and populate them in Views dropdown. 

//get all the views for the selected list
  private getViewsForSelectedList():Promise<any>{
    if (!this.properties.selectedList) {
      // resolve to empty options since no list has been selected
      return Promise.resolve();
    }
    //reset the view value
    this.properties.selectedView = undefined;
    var list = sp.web.lists.getByTitle(this.properties.selectedList);
    //get all the views of the selected list.
    return sp.web.lists.getByTitle(this.properties.selectedList).views.get().then((data) =>{
      console.log("Total number of views are " + data.length);
      return data;
    });
  }

Now, we have all the views retrieved we need to get all the fields for the selected view in the dropdown.


private getFieldsForSelectedView():Promise<any>{
    if (!this.properties.selectedView) {
      // resolve to empty options since no list has been selected
      return Promise.resolve();
    }
    const filter = `Hidden eq false and ReadOnlyField eq false`;
    //get all the fields of the selected view.
    return sp.web.lists.getByTitle(this.properties.selectedList).views.getByTitle(this.properties.selectedView).fields.select('Items').filter(filter).get().then((data) =>{
      console.log("Total number of fields are " + data.length);
      return data;
    });

  }

Once we have all the views and fields, we need to populate them to the dropdown. For fields we need to create the collection of PropertyPaneCheckbox object and add that to array.

protected onPropertyPaneFieldChanged(propertyPath: string, oldValue:any, newValue:any):void{
    if(propertyPath ==='listDropdown' && newValue){
      super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
      this.properties.selectedList = newValue;
      //get previously selected value
      const previousViewValue :string = this.properties.selectedView;
      //reset the view dropdown to clear the old values.
      this.viewDropDownOptions = [];
      this.fieldsListCollection =[];
      //reset the view value
      //this.properties.selectedView = undefined;
      this.onPropertyPaneFieldChanged('viewDropdown' , previousViewValue, this.properties.selectedView);
      this.getViewsForSelectedList().then((response) =>{
        for(let i=0 ; i< response.length;i++){
          //now populate the listdropdown array
         this.viewDropDownOptions.push({key:response[i].Title,text:response[i].Title});
        }
        //this.listsDropdownDisabled = false;
        this.context.propertyPane.refresh();
        this.context.statusRenderer.clearLoadingIndicator(this.domElement);
        this.render();
      });
    }
//get all the fields for a view and add to array
    else if(propertyPath==='viewDropdown' && newValue){
      this.properties.selectedView = newValue;
      this.getFieldsForSelectedView().then((response) => {
        const fields = (response as any).Items.results || (response as any).Items;
        this.fieldsListCollection =[];
        for(let f=0;f< fields.length;f++){
          this.fieldsListCollection.push(
            PropertyPaneCheckbox(
              fields[f],
              {text:fields[f]}
            )
          );
        }
        this.context.propertyPane.refresh();
      });
    }
    else {
      super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
    }
  }

Here is the final result how it will look like.



Happy coding..
Happy SharePointing...

Comments

Popular posts from this blog

Deploy artifacts with spfx webparts