Let's Play with SharePoint Taxonomy in SPFx

Taxonomy in the the Term Store of SharePoint is great. It enables to create a hierarchy of simple and friendly Urls.

Portal like Google site offer out of the box components that will display child term of a certen navigation term as url in the page, like that it will be super easy to create a contextual navigation.

The idea here is to have an equivalent webpart for SharePoint using a SPFx webpart.

So to sum up the need. We want a webpart that will display a list of child terms urls based on the current page or term set item.

First create a classic WebPart based on the React framework with yo.

The webpart .ts code is pretty straight forward, we just need to passe the siteurl from the context to our React element so in the render() function just add those lines

public render(): void {
    const element: React.ReactElement<ITaxonomyNavigationProps > = React.createElement(
      TaxonomyNavigation,
      {
        description: this.properties.description,
         siteUrl: this.context.pageContext.web.absoluteUrl
      }
    );

    ReactDom.render(element, this.domElement);
  }

Now in our React component, what we want to achieve is to get all the child term set items from our current term set item. To do that we will need to interact with the taxonomy service. First we need to load the taxonomy scripts, get a reference to the termstore and to the root termstore group with 

private _loadSPJSOMScripts() {
    const siteColUrl = Utils.getSiteCollectionUrl();
    try {
      SPComponentLoader.loadScript(siteColUrl + '/_layouts/15/init.js', {
        globalExportsName: '$_global_init'
      })
        .then((): Promise<{}> => {
          return SPComponentLoader.loadScript(siteColUrl + '/_layouts/15/MicrosoftAjax.js', {
            globalExportsName: 'Sys'
          });
        })
        .then((): Promise<{}> => {
          return SPComponentLoader.loadScript(siteColUrl + '/_layouts/15/SP.Runtime.js', {
            globalExportsName: 'SP'
          });
        })
        .then((): Promise<{}> => {
          return SPComponentLoader.loadScript(siteColUrl + '/_layouts/15/SP.js', {
            globalExportsName: 'SP'
          });
        })
        .then((): Promise<{}> => {
          return SPComponentLoader.loadScript(siteColUrl + '/_layouts/15/SP.taxonomy.js', {
            globalExportsName: 'SP'
          });
        })
        .then((): void => {
          this.setState({ loadingScripts: false });
          const context: SP.ClientContext = new SP.ClientContext(this.props.siteUrl);
          let taxSession =  SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
          let termStore  = taxSession.getDefaultSiteCollectionTermStore();
          let termGroups = termStore.get_groups();
          let termGroup = termGroups.getByName("YOUR_ROOT_TERMGROUP_NAME");
          let termSets = termGroup.get_termSets();
          this.loadTermStore(termSets, context);
        })
        .catch((reason: any) => {

        });
    } catch (error) {

    }
  }

Here Utils.getSiteCollectionUrl() is just a helper function to get the site collection url. Once we have loaded all the requered script and got our reference to our main TermGroup we can load and enum all the termset with

private loadTermStore(termSets: SP.Taxonomy.TermSetCollection,spContext:SP.ClientContext ){

    var reactHandler = this;

    let termSet = termSets.getByName("NAVIGATION");
    let terms = termSet.getAllTerms();

    spContext.load(terms, 'Include(Name, Parent, IsRoot,Id,PathOfTerm)');

    spContext.load(terms);
    spContext.load(termSet);
    let termStore:any[]=[];
    let childTerm:any[]=[];
    spContext.executeQueryAsync(function () {

      var termsEnum = terms.getEnumerator();


      while (termsEnum.moveNext()) {

        var spTerm = termsEnum.get_current();
        termStore.push({label:spTerm.get_name(),value:spTerm.get_name(), id:spTerm.get_id(), pathOfTerm:spTerm.get_pathOfTerm(),pathOfParentTerm:  spTerm.get_isRoot()?"":spTerm.get_parent().get_pathOfTerm()});

      }

      window['termStore']= termStore;

      let currentTermUrl = document.location.href.replace(document.location.search,'').replace(spContext.get_url()+'/','').replace(/\//g,';').replace(/-/g,' ').toLowerCase();
      termStore.filter((e) => e.pathOfParentTerm.toLowerCase() === currentTermUrl).forEach( term =>{
        childTerm.push({Url:reactHandler.props.siteUrl + "/" + term.pathOfTerm.replace(/;/g,'/').replace(/ /g,'-'),Description:term.value});
      });

      reactHandler.setState({
        items: childTerm
      });

    });
  }

Here in termSets.getByName("NAVIGATION") I am supposing that the root termset is called NAVIGATION but you can replace it with another name to fit your environment.

The ligne "let currentTermUrl = document.location.href.replace(document.location.search,'').replace(spContext.get_url()+'/','').replace(/\//g,';').replace(/-/g,' ').toLowerCase();" can surely be optimzed the idea is to get the name of the current term name like for example if you are viewing the page https://yourtenant.sharepoint.com/main/news then "main/news" is our current termname url.

After that just a filter to get only the child for this termname.

One more thing window['termStore']= termStore; can be reused as well to save time by not loading the hole termstore but I will leave it up to you guys :)

The complete script is available at https://github.com/alaabitar/spfx


 

Add comment