> ## Content Index
> Fetch the complete content index at: https://blog.lsonline.fr/llms.txt
> Use this file to discover other available public pages before exploring further.

# Display hierarchical Term Sets to your Web Part
- URL: https://blog.lsonline.fr/2019/01/13/display-hierarchical-term-sets-to-your-web-part/
- Published: 2019-01-13T22:21:39.000Z
- Updated: 2026-01-27T18:09:45.000Z
- Description: How to display a dynamic path selector from hierarchical Term Sets based on a SharePoint list/library
- Author: Laurent Sittler
- Tags: SharePoint, Development, Microsoft Patterns & Practices, JavaScript

You have a SharePoint list or a documents library in which you manage the content by managed metadata; you want to display the content of this list in accordance with a selected path chosen by the users. Natively, without additional features to the site or list, you can use:

1. The column header, directly from the list view

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-13-at-20.19.09.png)

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-13-at-20.22.52.png)

1. With the new experience, the filter panel

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-13-at-20.24.15-1.png)

However, you have no native Web Part that allows you to display a custom path selector.

Ok, but what for? The image you wish provides documentation about tools in accordance with a use case. The users follow the path of the use case and at the end, they should find all documents in accordance with the path. Like that:

![](https://blog.lsonline.fr/content/images/2019/01/LsOnline-FilesFilterTaxonomy.mp4-1.gif)

Once the Term is retrieved, it is possible to display the result in accordance with this one.

Below, the solution I have chosen:

## Get Taxonomy

[PnPjs](https://github.com/pnp/pnpjs?ref=blog.lsonline.fr) to retrieve a Term Sets of the current site based on a field from the list:

```javascript
sp.web.lists.getById('00000000-0000-0000-0000-000000000000').fields.getById('11111111-1111-1111-1111-111111111111').get().then((field) => {
    taxonomy.getDefaultSiteCollectionTermStore().getTermSetById(field.TermSetId).terms.get().then((t) => {
      console.log(t); /* HERE ALL TERMS FROM TERM SETS */
    });
});

```

The result is:

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-13-at-22.23.39.jpg)

## Transform data

Now, transform this result into an object that can be reused to display a tree. I chose the structure proposed by the framework [jsTree](https://www.jstree.com/docs/json/?ref=blog.lsonline.fr).

First, create a function that allows getting each individual term:

```javascript
public buildTree(ts: Array<any>): Array<any> {
    var tree = [];
    ts.forEach(term => {
        tree = this.branch(tree, term)
    });
    return tree;
}

```

`buildTree()` use the most important recursive function named `branch()` that allows to reproduce the hierarchy:

```javascript
private branch(tree: Array<any>, term: any, node: any = null, i: number = 0): Array<any> {
    var pathOfTerm = term.PathOfTerm.split(';');
    /* Switch level */
    var c = node == null ? tree : node;
    if (i < pathOfTerm.length) { /* Ensure not loop recursive */
        var r = c.find(function (obj) { return obj.text === pathOfTerm[i]; }); /* check if node already exist */
        if (r) { /* If exist */
            if ((i + 1) == pathOfTerm.length) { /* Update ID if node == current term */
                r.id = term.Id.replace('/Guid(', '').replace(')/', '');
            } else { /* recursive children */
                this.branch(tree, term, r.children, i + 1);
            }
        } else { /* Not exists, create new node */
            /* See : https://www.jstree.com/docs/json/ for all specification */
            var o = { "text": pathOfTerm[i], "id": null, "state": { "opened": false }, "children": [] };
            if ((i + 1) == pathOfTerm.length) { /* If it's a root node or last child, set ID */
                o.id = term.Id.replace('/Guid(', '').replace(')/', '');
            } else { /* recursive children */
                this.branch(tree, term, o.children, i + 1);
            }
            c.push(o);
        }
    }
    return c;
}

```

In this function, the following rows allow to define the template of each node :

```javascript
var o = { "text": pathOfTerm[i], "id": null, "state": { "opened": false }, "children": [] };

```

```javascript
o.id = term.Id.replace('/Guid(', '').replace(')/', '');

```

And here the result of the object model:

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-13-at-22.39.26.jpg)

With this kind of data object, you can directly use the jsTree render or create your own HTML structure like that :

![](https://blog.lsonline.fr/content/images/2019/01/Screenshot-2019-01-14-at-10.21.46.jpg)

## Get the selected (clicked) Term

Now, you probably want to get which term was selected, update the view and do something else...

To do this, add an `addEventListener()` on your render tree nodes *(in my case, I'm using the CSS class `termSelector`)* and attach the click event to the following function that returns the object node selected :

```JavaScript
public toggleSelectedTerm(tree: any, id: string): any {
    var n = tree.find(function (obj) { return obj.id === id; });
    if (!n) {
        tree.forEach(e => {
            if (!n && e.children.length > 0) {
                n = this.toggleSelectedTerm(e.children, id);
            }
        });
    }
    if (n && !n.edited) {
        n.state.opened = !n.state.opened;
        /* Add flag for avoid overwrite recursivity */
        n.edited = true;
    }
    return n;
}
```

> \[note\]**Note**  
>  
> Just be care that the event is bind only once.

If you use this function :

```JavaScript
var selectedNode = this.toggleSelectedTerm([buidTree return], [ID of selected term]);
```

don't forget to reset the flag added by the previous function

```JavaScript
delete n.edited;
```

## Next Step

Display the content of the list or documents library in accordance with the selected term.
  
  
Hoping this post will help you 🚀