8

The API I am working against gives me the following structure in response:

"data": [
{
  "id": 5,
  "name": "First name",
  "parent": 0
},
{
  "id": 1,
  "name": "Second name",
  "parent": 5
},
{
  "id": 6,
  "name": "Third name",
  "parent": 1
},
{
  "id": 15,
  "name": "Fourth name",
  "parent": 0
},
{
  "id": 25,
  "name": "Fifth name",
  "parent": 5
}
]

I would like to build a tree structure around this using ngFor that supports an unlimited number of children levels.

This is what I have tried so far:

<div *ngFor="let d1 of _dataList">
<ul *ngIf="d1.parent == 0">
    <li>
        {{d1.name}}
        <ul *ngFor="let d2 of _dataList">
            <li *ngIf="d2.parent == d1.id">{{d2.name}}</li>
        </ul>
    </li>
</ul>
</div>

That works, but it's ugly and I have to manually repeat this X-levels down the data and thus leaving a hard-coded limit.

How can one optimize this code to support unlimited levels - and look better?

Glenn Utter
  • 2,313
  • 7
  • 32
  • 44
  • 2
    This might help http://stackoverflow.com/questions/37746516/use-component-in-itself-recursively-to-create-a-tree/37747022#37747022 – Günter Zöchbauer Jan 30 '17 at 15:00
  • I would rather say it's a duplicate – smnbbrv Jan 30 '17 at 15:03
  • Possible duplicate of [Use component in itself recursively to create a tree](http://stackoverflow.com/questions/37746516/use-component-in-itself-recursively-to-create-a-tree) – smnbbrv Jan 30 '17 at 15:03
  • 1
    You'll want to format your data and use a recursive structure like the question linked above. You should probably format it so every node that has children has them in an array, and then you can pass data around more easily recursively. – Seiyria Jan 30 '17 at 15:23
  • Thanks Seiyria, I'll look into the possibility of having the API return the list in a different format. – Glenn Utter Jan 31 '17 at 06:58

2 Answers2

4

https://stackblitz.com/edit/angular-yij5e5?file=src%2Fapp%2Ftree-view%2Ftree-view.component.ts Note: below code is not recursive it is 2d and can't use for tree render. you should use ng template or defining a component for it.

<div *ngFor="let d1 of _dataList">
<ul *ngIf="d1.parent == 0">
    <li>
        {{d1.name}}
        <ul *ngFor="let d2 of _dataList">
            <li *ngIf="d2.parent == d1.id">{{d2.name}}</li>
        </ul>
    </li>
</ul>
</div>

[Solution 1]=>

JS

let xTree = [{
                "id": 5,
                "name": "First name",
                "parent": 0
            },
            ...
        ];
        let tree = [{
            id: 0,
            name: 'root',
            parent: null,
            childs: []
        }];
        let todoList = [];
        Converter();
        function FindParent(list, el) {
            if (list.length > 0) {
                let res = list.find(x => x.id === el.parent);
                if (res) {
                    return res;
                } else {
                    let _res = undefined;
                    list.forEach(xchild => {
                        _res = FindParent(xchild.childs, el);
                        if (res)
                            return _res;
                    });
                    return _res
                }
            } else {
                return undefined;
            }
        }

        function Converter() {
            todoList = xTree;
            for (let x = 0; x < 90; x++) {
                todoList.forEach(r => {
                    let parent = FindParent(tree, r);
                    if (parent) {
                        if (!parent.childs) {
                            parent.childs = [];
                        }
                        parent.childs.push(r);
                        todoList = todoList.filter(el => el !== r);
                    }
                });
            }
        }

html

<ul class="tree">
  <ng-template #recursiveList let-list="list">
    <li *ngFor="let item of list">
      {{item.name}}
      <ul *ngIf="item.childSet.length > 0">
        <ng-container *ngTemplateOutlet="recursiveList; context:{ list: item.data}"></ng-container>
      </ul>
    </li>
  </ng-template>
  <ng-container *ngTemplateOutlet="recursiveList; context:{ list: data}"></ng-container>
</ul>

css

ul.tree, ul.tree ul {
  list-style-type: none;
}
  ul.tree, ul.tree ul {
    list-style-type: none;
    background: url(/assets/vline.png) repeat-y;
    margin: 0;
    padding: 0;
  }

    ul.tree ul {
      margin-left: 10px;
    }

    ul.tree li {
      margin: 0;
      padding: 0 12px;
      line-height: 20px;
      background: url(/assets/node.png) no-repeat;
      color: #369;
      font-weight: bold;
    }

      ul.tree li:last-child {
        background: #fff url(/assets/lastnode.png) no-repeat;
      }
Navid Golforoushan
  • 728
  • 1
  • 9
  • 16
3

you can have a recusive component, say the component called TreeComponent and the template for TreeComponent will be like this

<div *ngFor="let item of dataList">
  <ul *ngIf="item.parent==parentId">
    <li>{{item.name}}
      <tree [parentId]="item.id" [dataList]="removeCurrentLevelItems(dataList,parentId)"></tree>
    </li>
  </ul>
</div>

check the link here for a live demo

lucas
  • 503
  • 2
  • 13
  • hi, lucas, I can not find a way to do editing from the live demo link. Could you please help ? – Feng Zhang May 01 '19 at 12:08
  • @FengZhang it doesnt require permissions etc to do editing, you can try to fork to your one or check your browser console to see any errors – lucas May 02 '19 at 22:27