Introduction
PnPjs is an awesome library that providers wrappers around SharePoint REST API so that developers do not have to write repetitive code and also don't have to worry about details on how to pass headers, data, etc. It is not only limited to SharePoint, but we can also use it to call a graph and Office 365 API. As this library comes as an npm package, it can be used for any node js and javascript based projects. You can learn more about PnP JS at this
link. There are lots of packages available within this library that can be used selectively.
A few days back, PnP JS version 2.0.6 was released which had new API/method supports for Taxonomy read operations. In this article, we will explore different Taxonomy Read(Terms) operations available and how to use them using PnP JS in React-based SPFx solutions, we will see an example of a web part but this can be used in extension in a similar way.
Step - Create a SPFx Solution
- md pnpjsoperations
- cd pnpjsoperations
Run the below commands in sequence. Open a command prompt and create a directory for the SPFx solution and go to that directory.
Let's now create our solution:
Select the below options.
We will be using the React framework here so that we can also explore react concepts. Once you select all options in wizard one by one, it will take some time to generate the code structure. You should see a success message once completed.
Step - Install PnP JS Library Package/s
Now let's install the PnPJS npm packages. For this sample, we will be using @sp package so we will only install sp package but you can install others also based on your requirement. Refer to this
link to find details list of packages available within the library
Run the below command.
- npm install @pnp/sp --save
After it is completed, open the same folder in Visual Studio code (you can use any other editor as well).
Step - Install JSON Viewer
We need this library just for this article's purpose. PnPJs will call SharePoint REST API which will return us JSON, so in order to view this JSON object, we will use this react based library which displays JSON object in a nice and readable format. You can read about this package at this
link.
Now let's modify code to proceed with the demo. If you want to know about how to get started with React in SPFx web part, you can check my webinar on same at this
link
Step - Passing Web part Context to React Component
PnP JS needs a Sharepoint site context to work with, therefore we will pass it from our web part file to react components.
Open src\webparts\controls\components\ISampleDemoProps.ts
Modify the code as shown below.
- import { WebPartContext } from "@microsoft/sp-webpart-base";
- export interface ISampleDemoProps {
- description: string;
- spcontext:WebPartContext;
- }
Open src\webparts\controls\ControlsWebPart.ts
Modify the render method to pass the context.
- public render(): void {
- const element: React.ReactElement < ISampleDemoProps > = React.createElement(SampleDemo, {
- description: this.properties.description,
- spcontext: this.context
- });
- ReactDom.render(element, this.domElement);
- }
Please note we have just added line ‘spcontext:this.context’ .
Step - Modify React Component
Below are the high-level steps that we will do.
- Import required library, in our case, we will be using PnP package and buttons from office UI/Fluent react and also a json-view library to display response from PnP Js methods to display in a readable format.
- Create a state interface, properties will be used to store JSON response from PnP JS methods and which will be bound to JSON view control.
- Constructor to initialize PnP JS context and state.
- The render method has been modified to add 5 buttons to demonstrate the respective use case.
- 5 methods that are called based on button selection.
- 3 Text fields to allow the user to input Term Group ID, Term Set ID, and Term ID
- Calling PnP JS methods and changing the state variable jsonResponse.
Open src\webparts\controls\components\SampleDemo.tsx
- import * as React from 'react';
- import styles from './SampleDemo.module.scss';
- import { ISampleDemoProps } from './ISampleDemoProps';
- import { escape } from '@microsoft/sp-lodash-subset';
-
-
- import { PrimaryButton, Stack,MessageBar, MessageBarType } from 'office-ui-fabric-react';
- import { TextField, MaskedTextField } from 'office-ui-fabric-react/lib/TextField';
- import { sp, ITermGroupInfo, ITermSetInfo, ITermInfo } from "@pnp/sp/presets/all";
- import ReactJson from 'react-json-view';
-
-
- export interface ISampleDemoState {
- jsonResponse:any;
- termGroupID:any;
- responseOf:string;
- termSetID:any;
- Title:any;
- termID:any;
- }
-
- export default class SampleDemo extends React.Component<ISampleDemoProps, ISampleDemoState> {
-
-
- constructor(props: ISampleDemoProps,state:ISampleDemoState) {
- super(props);
- this.state = {jsonResponse:null,
- termGroupID:"9791111-123-1111-213-a8512123123ab399",responseOf:"",
- termSetID:"123123321-123-123-a4d7-b91232131236f",
- termID:"12312312312-123-123-123-123123123231",
- Title:null};
- sp.setup({
- spfxContext: this.props.spcontext
- });
- spObj = sp;
- }
-
- public render(): React.ReactElement<ISampleDemoProps> {
- return (
- <div className={ styles.sampleDemo }>
- <div className={ styles.container }>
- <div className={ styles.row }>
- <div className={ styles.column }>
- <span className={ styles.title }>Welcome to PnP JS Taxonomy Read Operations Demo!</span>
- </div>
- </div>
- </div>
- <br></br>
- <TextField value={this.state.termGroupID} label="Enter Group ID" onChange={(e)=> this.setGroupID(e.target)}/>
- <br></br>
- <TextField value={this.state.termSetID} label="Enter Term Set ID" onChange={(e)=> this.setTermSetID(e.target)}/>
- <br></br>
-
- <TextField value={this.state.termID} label="Enter Term ID" onChange={(e)=> this.setTermID(e.target)}/>
- <br></br>
-
- <Stack horizontal tokens={{childrenGap:40}}>
- <PrimaryButton text="Get Term Group" onClick={()=>this.getTermGroup()} />
- <PrimaryButton text="Get Term Sets of Group" onClick={()=>this.getTermSetsofGroups()} />
- <PrimaryButton text="Get Term Set By ID" onClick={()=>this.getTermSetById()} />
- </Stack>
- <br></br>
- <Stack horizontal tokens={{childrenGap:40}}>
- <PrimaryButton text="Get Terms" onClick={()=>this.getTerms()} />
- <PrimaryButton text="Get Term By ID" onClick={()=>this.getTermById()} />
- </Stack>
- <br></br>
- <br></br>
- {this.state.jsonResponse &&
- <React.Fragment>
- <div>Response from: {this.state.responseOf}</div>
- <br></br>
- <ReactJson src={this.state.jsonResponse} collapsed={false} displayDataTypes={false} displayObjectSize={false}/>
- </React.Fragment>
- }
- </div>
- );
- }
-
- private setGroupID(element) {
- var val = (element as HTMLInputElement).value;
- this.setState({termGroupID:val});
- }
-
- private setTermSetID(element) {
- var val = (element as HTMLInputElement).value;
- this.setState({termSetID:val});
- }
-
- private setTermID(element) {
- var val = (element as HTMLInputElement).value;
- this.setState({termID:val});
- }
-
- private async getTermGroup(){
- const info: ITermGroupInfo = await sp.termStore.termGroups.getById(this.state.termGroupID)();
- this.setState({jsonResponse:info,responseOf:"Get Terms Group"});
- }
-
- private async getTermSetsofGroups(){
- const info: ITermSetInfo[] = await sp.termStore.termGroups.getById(this.state.termGroupID).termSets();
- this.setState({jsonResponse:info,responseOf:"Get Term Sets of Groups"});
- }
-
- private async getTermSetById(){
- const info: ITermSetInfo[] = await await sp.termStore.groups.
- getById(this.state.termGroupID).sets.
- getById(this.state.termSetID)();
- this.setState({jsonResponse:info,responseOf:"Get Term Set By ID"});
- }
-
- private async getTerms(){
- const info: ITermInfo[] = await sp.termStore.termGroups.getById(this.state.termGroupID)
- .termSets.getById(this.state.termSetID).terms();
- this.setState({jsonResponse:info,responseOf:"Get Terms of Term Set"});
- }
-
- private async getTermById(){
- const info: ITermSetInfo[] = await sp.termStore.
- groups.getById(this.state.termGroupID).
- sets.getById(this.state.termSetID)
- .terms.getById(this.state.termID)();
- this.setState({jsonResponse:info,responseOf:"Get Term By Id"});
- }
- }
Notes about code
- I am using Json Viewer to display the output of the API. You can just using console.log or alert method for your demo purpose.
- It is important to understand the hierarchy
- Taxonomy services can have multiple Groups, A groups can have multiple Terms sets, and Term sets can have multiple Terms.
- Hence to get a Term we will have to also pass parent objects ids like term set and Group Id where term belongs. For better understanding check getTermById method. If you see we had to first get group by id, then get terms set by id and then finally term. Though it looks like we are getting multiple API, in fact, it is just a single REST API call that is being made.
Step - Testing the webpart
Let us see this web part in the action. Run gulp serve
Open the SharePoint workbench page.
Add a target web part, and when the page loads, we will see the below output:
Let's now see how to get Group ID, Term Set ID, and Term ID.
Copy the unique identifier of Group ID, Term Set ID, and Term from SharePoint Admin Center(Term Store section) for our demonstration purpose.
In my case, I have initialized the respective state variable with the IDs so that I don't have to enter again and again. However, it will always pick up the latest one that you will enter in text boxes. Make sure you enter the required information.
Now let's see the output of operations, one by one
Click on the Get Term Group button:
Click on Get Term Set of Group:
Click on Get Term Set By Id:
Click on Get Terms:
Click on Get Term By Id:
Conclusion
In this article, we have explored the below concepts:
- Calling PnP JS Taxonomy API/methods.
- Using React concepts in the SPFx web part.
- Using Office Fabric/Fluent UI control in the web part.
- Using external React Jsonviewer library for visualization of JSON output.
I hope you enjoyed reading... Happy coding!!!