First, you need to convert your self-reference table to a hierarchical table (tree). I suggest you use a custom pipe to do this since you will be able to reuse this pipe in other places.
You can use Reactgular's code, my code from the StackOverflow thread, or write your own code. I create my converter
pipe with Reactgular's code:
converter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'converter'
})
export class ConverterPipe implements PipeTransform {
transform(array: any[], id: string = 'uid', parentId: string = 'pid'): any[] {
const map = array.reduce(
(acc, node) => ((node.items = []), (acc[node[id]] = node), acc),
{}
);
return Object.values(map)
.map(
node => (node[parentId] && map[node[parentId]].items.push(node), node)
)
.filter(node => node[parentId] === null);
}
}
Don't forget to add it to the declaration
section of your module:
app.module.ts
import { ConverterPipe } from './converter.pipe';
@NgModule({
declarations: [
ConverterPipe
]
})
export class AppModule { }
Now, you can create your component template and use the approach from the Hierarchy View CodePen. Since you need different markup for branches and leaves, it's handy to use NgTemplateOutlet
's and NgIf
structural directives. This's a good idea to move the level markup in a template and reuse it when you need to render a tree in Angular. The same idea is illustrated in my answer. Based on the provided CodePen code, your Angular markup may look as the following:
app.component.html
<div class="hv-wrapper">
<ng-template #Item let-item>
<ng-container *ngIf="!item.items.length; else Component">
<p>{{ item.uid }}</p>
</ng-container>
<ng-template #Component>
<div class="hv-item">
<div class="hv-item-parent">
<p>{{ item.uid }}</p>
</div>
<div class="hv-item-children">
<div class="hv-item-child" *ngFor="let child of item.items">
<ng-container
*ngTemplateOutlet="Item; context: { $implicit: child }"
></ng-container>
</div>
</div>
</div>
</ng-template>
</ng-template>
<ng-container *ngFor="let child of response | converter"
><ng-container
*ngTemplateOutlet="Item; context: { $implicit: child }"
></ng-container
></ng-container>
</div>
Here, response
is your original array:
app.component.ts
export class AppComponent {
response = [
{ uid: 'abc', pid: null, depth: 1, parent: true },
{ uid: 'def', pid: 'abc', depth: 2, parent: true },
{ uid: 'ghi', pid: 'abc', depth: 2, parent: false },
{ uid: 'jkl', pid: 'ghi', depth: 3, parent: false },
{ uid: 'mno', pid: 'ghi', depth: 3, parent: false }
];
}
Don't forget to use the CodePen SASS styles in your project.
After this, you will a graph like:

This is a StackBlitz project that demonstrates this approach in action.