0

Im using this tutorial https://www.c-sharpcorner.com/article/how-to-use-pnp-react-accordion-control-in-spfx/ to display items from an SPO list in a React Accordion.

It's hardcoded to use the columns "Title" and "Description"

Would it be possible to have two properties where users could specify the columns to use?

Thanks P

    return (  
      <div className={styles.pnpReactAccordion}>  
        {  
          //Map list items and render in accordion  
          (this.state.listItems && this.state.listItems.length) ? this.state.listItems.map((item, index) => (  
            <Accordion title={item.Title} defaultCollapsed={true} className={"itemCell"} key={index}>  
              <div className={"itemContent"}>  
                <div className={"itemResponse"} dangerouslySetInnerHTML={{ __html: item.Description }}></div>  
              </div>  
            </Accordion>  
          )) : <p>{this.state.errorMessage}</p>  
        }  
      </div>  
    );  
  }  



 interface IListItem { Id?: string; Question: string; Answer: string } export interface IReactAccordionState { listItems: IListItem[]; errorMessage: string; }


private async getListItems() { if (this.props.listName) { let items = await this._services.getListItems(this.props.listName, this.props.camlQuery); this.setState({ listItems: items }); } else { this.setState({ errorMessage: 'Please enter the list name in property pane configuration.' }); } }




SPService.ts

import { WebPartContext } from "@microsoft/sp-webpart-base";  
//import { sp } from '@pnp/sp/presets/all';  
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
  

export class SPService {  
    constructor(private context: WebPartContext) {  
        // sp.setup({  
        //     spfxContext: this.context  
        // });  
    }  
  
    public async getListItems(listName: string, camlQuery: string) {  
        try {  
            // let listItems: any[] = await sp.web.lists.getByTitle(listName)  
            //     .items  
            //     .select("Id,Title,Description")  
            //     .get();  
            // return listItems;  

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

            //const items: any[] = await sp.web.lists.getByTitle(listName).items();
            
            //const items: any[] = await sp.web.lists.getByTitle(listName).getItemsByCAMLQuery({
            //  ViewXml: `<View><Query><Where><Eq><FieldRef Name="Category"></FieldRef><Value Type="Text">Choice 1</Value></Eq></Where></Query></View>`,

            const items: any[] = await sp.web.lists.getByTitle(listName).getItemsByCAMLQuery({
             //ViewXml: `<View><Query><Where><Eq><FieldRef Name="Category"></FieldRef><Value Type="Text">Choice 1</Value></Eq></Where></Query></View>`,
               ViewXml: camlQuery,

          });

            console.log(items);
            return items;
        } catch (err) {  
            Promise.reject(err);  
        }  
    }  
}  
13
  • 1
    Yes. this is possible. You can create two property in web part property pane and adjust your code accordingly. Follow docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/basics/… link to add web part property pane Sep 6, 2022 at 12:36
  • Thanks I try to do this but it doesn't like it ... <Accordion title={item[this.props.headerColumn]} defaultCollapsed={true} className={"itemCell"} key={index}> <div className={"itemContent"}> <div className={"itemResponse"} dangerouslySetInnerHTML={{ __html: item.Answer }}></div> </div> </Accordion> Sep 6, 2022 at 15:35
  • Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'IListItem'. No index signature with a parameter of type 'string' was found on type 'IListItem'.ts(7053) Sep 6, 2022 at 15:35
  • But it is fine with ... <Accordion title={item["Question"]} defaultCollapsed={true} className={"itemCell"} key={index}> Sep 6, 2022 at 15:36
  • Could you please share a code for State interface and also code which you are using to get items from SharePoint list to get more idea? Sep 8, 2022 at 6:16

1 Answer 1

1

You should update getListItems method as below:

private async getListItems() {
    if (this.props.listName) {
      const listItems = await this._services.getListItems(this.props.listName, this.props.camlQuery);
      const items = new Array<IListItem>();
      for (let index = 0; index < listItems.length; index++) {
        const item = listItems[index];
        items.push({
          Id: item["ID"],
          Question:item[this.props.headerColumn],
          Answer: item[this.props.descriptionColumn]
        });        
      }
      this.setState({ listItems: items });
    }
    else {
      this.setState({ errorMessage: 'Please enter the list name in property pane configuration.' });
    }
  } 

I took the assumption here that this.props.headerColumn and this.props.descriptionColumn will be password from parent from web part property configuration.

Update IListItem interface to export it so that we can use it in another field. Add "export" line before interface as shown in below

 export interface IListItem { Id?: string; Question: string; Answer: string } export interface IReactAccordionState { listItems: IListItem[]; errorMessage: string; }

Update import statement in the file where you have written getItems method.

import { IPnpReactAccordionState, IListItem } from './IPnpReactAccordionState';  

Then you can update your render method as below:

return (  
  <div className={styles.pnpReactAccordion}>  
    {  
      //Map list items and render in accordion  
      (this.state.listItems && this.state.listItems.length) ? this.state.listItems.map((item, index) => (  
        <Accordion title={item.Question} defaultCollapsed={true} className={"itemCell"} key={index}>  
          <div className={"itemContent"}>  
            <div className={"itemResponse"} dangerouslySetInnerHTML={{ __html: item.Answer }}></div>  
          </div>  
        </Accordion>  
      )) : <p>{this.state.errorMessage}</p>  
    }  
  </div>  
);  
3
  • Thanks. On the line const items = new Array<IListItem>(); it says Cannot find name IListItem Sep 8, 2022 at 11:12
  • Updated my answer accordingly Sep 8, 2022 at 11:20
  • You are a star. Thanks so much. Sep 8, 2022 at 11:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.