SharePoint Taxonomy Read Operations Using PnPJS

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
  1. md pnpjsoperations  
  2. 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:
  1. yo @microsoft/sharepoint   
Select the below options.
 
SharePoint Taxonomy Read Operations Using PnPJS
 
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.
  1. npm install @pnp/sp --save
SharePoint Taxonomy Read Operations Using PnPJS
 
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.
  1. npm i react-json-view
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.
  1. import { WebPartContext } from "@microsoft/sp-webpart-base";  
  2. export interface ISampleDemoProps {  
  3.    description: string;  
  4.    spcontext:WebPartContext;  
  5. }   
Open src\webparts\controls\ControlsWebPart.ts
 
Modify the render method to pass the context.
  1. public render(): void {  
  2.     const element: React.ReactElement < ISampleDemoProps > = React.createElement(SampleDemo, {  
  3.         description: this.properties.description,  
  4.         spcontext: this.context  
  5.     });  
  6.     ReactDom.render(element, this.domElement);  
  7. }   
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 
  1. import * as React from 'react';  
  2. import styles from './SampleDemo.module.scss';  
  3. import { ISampleDemoProps } from './ISampleDemoProps';  
  4. import { escape } from '@microsoft/sp-lodash-subset';  
  5.   
  6. //import library  
  7. import {  PrimaryButton, Stack,MessageBar, MessageBarType } from 'office-ui-fabric-react';  
  8. import { TextField, MaskedTextField } from 'office-ui-fabric-react/lib/TextField';  
  9. import { sp, ITermGroupInfo, ITermSetInfo, ITermInfo } from "@pnp/sp/presets/all";  
  10. import ReactJson from 'react-json-view';  
  11.   
  12. //create state  
  13. export interface ISampleDemoState {  
  14.  jsonResponse:any;  
  15.  termGroupID:any;  
  16.  responseOf:string;  
  17.  termSetID:any;  
  18.  Title:any;  
  19.  termID:any;  
  20. }  
  21.   
  22. export default class SampleDemo extends React.Component<ISampleDemoProps, ISampleDemoState> {  
  23.   
  24.   // constructor to intialize state and pnp sp object.  
  25.   constructor(props: ISampleDemoProps,state:ISampleDemoState) {  
  26.     super(props);  
  27.     this.state = {jsonResponse:null,  
  28.       termGroupID:"9791111-123-1111-213-a8512123123ab399",responseOf:"",  
  29.       termSetID:"123123321-123-123-a4d7-b91232131236f",  
  30.       termID:"12312312312-123-123-123-123123123231",  
  31.       Title:null};  
  32.     sp.setup({  
  33.       spfxContext: this.props.spcontext  
  34.     });  
  35.     spObj = sp;  
  36.   }  
  37.   
  38.   public render(): React.ReactElement<ISampleDemoProps> {  
  39.     return (  
  40.       <div className={ styles.sampleDemo }>  
  41.         <div className={ styles.container }>  
  42.           <div className={ styles.row }>  
  43.             <div className={ styles.column }>  
  44.               <span className={ styles.title }>Welcome to PnP JS Taxonomy Read Operations Demo!</span>  
  45.             </div>  
  46.           </div>  
  47.         </div>  
  48.         <br></br>  
  49.         <TextField value={this.state.termGroupID} label="Enter Group ID" onChange={(e)=> this.setGroupID(e.target)}/>  
  50.         <br></br>  
  51.         <TextField value={this.state.termSetID} label="Enter Term Set ID" onChange={(e)=> this.setTermSetID(e.target)}/>  
  52.         <br></br>  
  53.           
  54.         <TextField value={this.state.termID} label="Enter Term ID" onChange={(e)=> this.setTermID(e.target)}/>  
  55.         <br></br>  
  56.   
  57.         <Stack horizontal tokens={{childrenGap:40}}>    
  58.                 <PrimaryButton text="Get Term Group" onClick={()=>this.getTermGroup()}  />    
  59.                 <PrimaryButton text="Get Term Sets of Group" onClick={()=>this.getTermSetsofGroups()} />    
  60.                 <PrimaryButton text="Get Term Set By ID" onClick={()=>this.getTermSetById()} />    
  61.              </Stack>    
  62.              <br></br>  
  63.              <Stack horizontal tokens={{childrenGap:40}}>    
  64.                 <PrimaryButton text="Get Terms" onClick={()=>this.getTerms()} />    
  65.                 <PrimaryButton text="Get Term By ID" onClick={()=>this.getTermById()} />   
  66.              </Stack>    
  67.             <br></br>  
  68.             <br></br>  
  69.         {this.state.jsonResponse &&  
  70.           <React.Fragment>  
  71.             <div>Response from: {this.state.responseOf}</div>  
  72.             <br></br>  
  73.             <ReactJson src={this.state.jsonResponse}  collapsed={false} displayDataTypes={false} displayObjectSize={false}/>  
  74.             </React.Fragment>  
  75.         }  
  76.       </div>  
  77.     );  
  78.   }  
  79.   
  80.   private setGroupID(element) {  
  81.     var val = (element as HTMLInputElement).value;  
  82.     this.setState({termGroupID:val});  
  83.   }  
  84.   
  85.   private setTermSetID(element) {  
  86.     var val = (element as HTMLInputElement).value;  
  87.     this.setState({termSetID:val});  
  88.   }  
  89.   
  90.   private setTermID(element) {  
  91.     var val = (element as HTMLInputElement).value;  
  92.     this.setState({termID:val});  
  93.   }  
  94.   
  95.   private async getTermGroup(){  
  96.     const info: ITermGroupInfo = await sp.termStore.termGroups.getById(this.state.termGroupID)();  
  97.     this.setState({jsonResponse:info,responseOf:"Get Terms Group"});  
  98.   }  
  99.   
  100.   private async getTermSetsofGroups(){  
  101.     const info: ITermSetInfo[] = await sp.termStore.termGroups.getById(this.state.termGroupID).termSets();  
  102.     this.setState({jsonResponse:info,responseOf:"Get Term Sets of Groups"});  
  103.   }  
  104.   
  105.   private async getTermSetById(){  
  106.     const info: ITermSetInfo[] = await await sp.termStore.groups.  
  107.     getById(this.state.termGroupID).sets.  
  108.     getById(this.state.termSetID)();  
  109.     this.setState({jsonResponse:info,responseOf:"Get Term Set By ID"});  
  110.   }  
  111.   
  112.   private async getTerms(){  
  113.     const info: ITermInfo[]  = await sp.termStore.termGroups.getById(this.state.termGroupID) // This is Term Group ID  
  114.     .termSets.getById(this.state.termSetID).terms(); //this is  Term Set ID  
  115.     this.setState({jsonResponse:info,responseOf:"Get Terms of Term Set"});  
  116.   }  
  117.   
  118.   private async getTermById(){  
  119.     const info: ITermSetInfo[] = await sp.termStore.  
  120.     groups.getById(this.state.termGroupID).  
  121.     sets.getById(this.state.termSetID)  
  122.     .terms.getById(this.state.termID)();  
  123.     this.setState({jsonResponse:info,responseOf:"Get Term By Id"});  
  124.   }  
  125. }  
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
  1. gulp serve
Open the SharePoint workbench page.
  1. https://msbuild.com/sites/mylab/_layouts/15/workbench.aspx
Add a target web part, and when the page loads, we will see the below output:
 
SharePoint Taxonomy Read Operations Using PnPJS
 
Let's now see how to get Group ID, Term Set ID, and Term ID. 
 
Go here.
 
SharePoint Taxonomy Read Operations Using PnPJS
 
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:
 
SharePoint Taxonomy Read Operations Using PnPJS
 
Click on Get Term Set of Group:
 
SharePoint Taxonomy Read Operations Using PnPJS
 
Click on Get Term Set By Id:
 
SharePoint Taxonomy Read Operations Using PnPJS
 
Click on Get Terms:
 
SharePoint Taxonomy Read Operations Using PnPJS
 
Click on Get Term By Id:
 
SharePoint Taxonomy Read Operations Using PnPJS
 

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!!!