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
  1. With the new experience, the filter panel

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:

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 🌳 to retrieve a Term Sets of the current site based on a field from the list:

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 😅🙄:

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.

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

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:

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 :

var o = { "text": pathOfTerm[i], "id": null, "state": { "opened": false }, "children": [] };
o.id = term.Id.replace('/Guid(', '').replace(')/', '');

And here the result of the object model 💪🏻 :

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

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 :

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 :

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

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

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 😉

You may also be interested in