To start, the first step is to transform your self-reference table into a hierarchical table (tree). I recommend utilizing a custom pipe for this task as it allows for reusability in various contexts.
You have options such as using Reactgular's code, my code from a previous StackOverflow thread, or crafting your own implementation. Personally, I crafted a converter
pipe leveraging 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);
}
}
Remember to include this pipe in the declaration
section of your module:
app.module.ts
import { ConverterPipe } from './converter.pipe';
@NgModule({
declarations: [
ConverterPipe
]
})
export class AppModule { }
With this setup, you can proceed to create your component template following the example provided in a CodePen demonstration like Hierarchy View. For cases requiring distinct markup for branches and leaves, leverage NgTemplateOutlet
and NgIf
directives. Centralizing the tree rendering logic in a template enhances reusability as demonstrated in my referenced solution.
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>
In this code snippet, use the original array response
:
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 overlook the inclusion of the CodePen SASS styles into your project.
Upon completion, your generated graph will resemble the one outlined below:
https://i.sstatic.net/pKOC2.png
For a practical demonstration, refer to this StackBlitz project showcasing this approach in action.