8

I have a role object that I wanted to map to a TreeNode object using PrimeNG to display it in a tree. The role object is something like this (shown in the picture as well)

role: [
id: ....
name: ....
   description: ....
   roles[]: .....
]

role object

The tree node object has the following structure is:

{
"data": 
[
    {
        "label": "Documents",
        "data": "Documents Folder",
        "expandedIcon": "fa-folder-open",
        "collapsedIcon": "fa-folder",
        "children": [{
                "label": "Work",
                "data": "Work Folder",
                "expandedIcon": "fa-folder-open",
                "collapsedIcon": "fa-folder",
                "children": [{"label": "Expenses.doc", "icon": "fa-file-word-o", "data": "Expenses Document"}, {"label": "Resume.doc", "icon": "fa-file-word-o", "data": "Resume Document"}]
            },
            {
                "label": "Home",
                "data": "Home Folder",
                "expandedIcon": "fa-folder-open",
                "collapsedIcon": "fa-folder",
                "children": [{"label": "Invoices.txt", "icon": "fa-file-word-o", "data": "Invoices for this month"}]
            }]
    },
    {
        "label": "Pictures",
        "data": "Pictures Folder",
        "expandedIcon": "fa-folder-open",
        "collapsedIcon": "fa-folder",
        "children": [
            {"label": "barcelona.jpg", "icon": "fa-file-image-o", "data": "Barcelona Photo"},
            {"label": "logo.jpg", "icon": "fa-file-image-o", "data": "PrimeFaces Logo"},
            {"label": "primeui.png", "icon": "fa-file-image-o", "data": "PrimeUI Logo"}]
    },
    {
        "label": "Movies",
        "data": "Movies Folder",
        "expandedIcon": "fa-folder-open",
        "collapsedIcon": "fa-folder",
        "children": [{
                "label": "Al Pacino",
                "data": "Pacino Movies",
                "children": [{"label": "Scarface", "icon": "fa-file-video-o", "data": "Scarface Movie"}, {"label": "Serpico", "icon": "fa-file-video-o", "data": "Serpico Movie"}]
            },
            {
                "label": "Robert De Niro",
                "data": "De Niro Movies",
                "children": [{"label": "Goodfellas", "icon": "fa-file-video-o", "data": "Goodfellas Movie"}, {"label": "Untouchables", "icon": "fa-file-video-o", "data": "Untouchables Movie"}]
            }]
    }
]

}

 data.roles.forEach((role, index) => {
        //this.roleTree.label = role.Name;
        //this.roleTree.data = role.ID;

        let treeNode: TreeNode = {
            label: role.Name,
            data: role

        }

        this.treeNodes.push(treeNode);
        console.log(role);
        console.log(index);

    });

But this code seems to be too complex when I try 'roles' in 'role' to map to 'children' in treeNode. I have seen some examples like this but it is mapping the same field names.

I'm new to Typesript, is there a workaround to convert my role object with roles to treeNode with children by specifying my fieldname (e.g. name) to be mapped to 'label' of role?

A code example would be highly appreciated.

sam
  • 391
  • 1
  • 5
  • 19
  • Do you have an `interface` for your role type or the `TreeNode` type? Does `TreeNode` allow an arbitrary object for its `data` property, and is its `children` an array of `TreeNode`s? – jcalz Sep 01 '17 at 19:28
  • and what is the type of the elements of the `roles` property? Are they the same type as the `role` object? – jcalz Sep 02 '17 at 00:06

1 Answers1

14

Well, I'm not 100% sure about the Role and TreeNode types you're using. Here's my guess based on the code in your question.

Role has a few properties you care about, but the tricky one is the roles property, which I am guessing is meant to be an array of Role objects:

interface Role {
  id: string;
  name: string;
  description: string;
  roles: Role[];
}

Likewise, TreeNode has some properties, but the tricky one is the children property which is an array of TreeNode objects. Also, I'm assuming that TreeNode is generic in the type of data, and in this case you want to do TreeNode<Role>, meaning the data property will be a Role object:

interface TreeNode<T> {
  label: string;
  data: T;
  expandedIcon?: string;
  collapsedIcon?: string;
  children: TreeNode<T>[];
}

If those are correct, then you can use the following roleToTreeNode() function to map a Role object to a TreeNode<Role> object:

function roleToTreeNode(role: Role): TreeNode<Role> {
  return {
    label: role.name,
    data: role,
    children: role.roles.map(roleToTreeNode)
  };
}

The children: line is the operative part of the function: you are taking the role.roles array, and mapping each Role element to a TreeNode<Role>, which is what the roleToTreeNode() function does. That is, roleToTreeNode() is a recursive function which calls itself.

Does this make sense? Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • Looks good, but let me give it a try and will get back to you. – sam Sep 03 '17 at 23:15
  • This works like a charm thanks alot. But i have another question. Could you explain how roleToTreeNode works? I have a lot of Children within Children Arrays how does it map everything to 1 Element? – TheWandererr Apr 18 '18 at 12:06
  • It's [recursive](https://en.wikipedia.org/wiki/Recursion#In_computer_science). The body of `roleToTreeNode()` creates an object literal whose `children` property is assembled by calling `roleToTreeNode` on each element of `role.roles`. You could do it iteratively with a `for` loop instead of using the `map()` array method, but it's the same thing. Recursion. – jcalz Apr 18 '18 at 17:49
  • @jcalz here is my input response expecting the same output as in the question. `[{ "$id": "1", "cityId": 2, "cityName": "India", "Description": "India", "parentId": 0, "cities": [{ "$id": "2", "cityId": 3, "cityName": "delhi", "Description": "delhi", "parentId": 1, "cities": [ {child}, {child} ] } ] }]` – Irfan Syed May 10 '18 at 20:00
  • [link](https://stackoverflow.com/questions/49029918/angular-primeng-treenode-convert-class-into-treenode-cannot-read-property-map) the following link helped me getting the recursion done and able to get my response – Irfan Syed May 11 '18 at 06:01