I have the below structure:
app-component --> based on the value received do-something
- side-resultview.component --> Emits a value to app-component
- collapsible-tree.component --> Emits a value to side-resultview.component
Where I am trying to display a collapsible tree based on some JSON returned from a service. I have another requirement to do something based which node/sub-node is clicked by user, so I am emitting a value (ID key) to the parent and that is again emitted by to the parent of it as shown in the above structure.
Issue: This is working fine only on the first level node i.e. only while clicking on Column Name 1, Column Name 2, Column Name 3, Column Name 4 and Column Name 5 but not the child-nodes in the tree. See the JSON below:
{
"results": [
{
"id": "1001",
"title": "Column Name 1",
"child": {
"results": [
{
"id": "10001",
"title": "Column Name 10",
"child": {
"results": [],
"size": 0
}
},
{
"id": "10002",
"title": "Column Name 11",
"child": {
"results": [
{
"id": "100011",
"title": "Column Name 101",
"child": {
"results": [],
"size": 0
}
},
{
"id": "100022",
"title": "Column Name 111",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
}],
"size": 2
}
},
{
"id": "1002",
"title": "Column Name 2",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1003",
"title": "Column Name 3",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1004",
"title": "Column Name 4",
"child": {
"results": [
{
"id": "100041",
"title": "Column Name 401",
"child": {
"results": [
{
"id": "1000041",
"title": "Column Name 4011",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1000042",
"title": "Column Name 4111",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
},
{
"id": "100042",
"title": "Column Name 411",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
},
{
"id": "1005",
"title": "Column Name 5",
"child": {
"results": [],
"size": 0
}
}],
"size": 5
}
Detailed code is as below:
The collapsible-tree.component.ts:
import { Component, Input } from '@angular/core';
import { Output, EventEmitter } from '@angular/core';
import { Observable } from "rxjs";
@Component({
selector: '[recursive]',
templateUrl: './collapsible-tree.component.html',
styleUrls: ['./collapsible-tree.component.css']
})
export class CollapsibleTreeComponent {
self = this;
@Output()
public nodeTokenValue = new EventEmitter<any>();
constructor() { }
@Input() level: number | undefined;
@Input() children: any;
@Input() parent: any;
@Input() search: (() => Observable<any>) | undefined;
expandNode(_item: { isOpen: boolean; }): void {
_item.isOpen = !_item.isOpen;
}
public nodeItemNameClicked(_item: any): void {
this.nodeTokenValue.emit(_item.id); // Emitting the ID key of the node
}
}
The collapsible-tree.component.html
<ul class="tree" *ngIf="level==undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ul>
<ng-container *ngIf="level!=undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ng-container>
<ng-template #tree let-children="children" let-search="search">
<li *ngFor="let item of children">
<span [ngClass]="item.child.size>0?'plus-node':'close-node'">
<a (click)="item.child.size>0 && expandNode(item)">
<i [ngClass]="item.child.size>0?'bi bi-plus-square-fill':'bi bi-dash-square'"></i>
</a>
<!-- This is a click event applied on the nodes -->
<a (click)="nodeItemNameClicked(item)"> {{item.title}}</a>
</span>
<ul recursive *ngIf="item.child.size>0 && item.isOpen"
[children]="item.child.results"
[parent]="self"
[level]="level!=undefined?level+1:0"
[search]="search">
</ul>
</li>
</ng-template>
Which generates a Collapsible Tree as below:
The side-resultview.component.html:
<div recursive [children]="searchResultJson"
[search]="commonSettingsService.getElasticResult"
(nodeTokenValue)="getNodeTokenValue($event)">
</div>
The side-resultview.component.ts:
import { Component, OnInit, ViewChild } from '@angular/core';
import { Output, EventEmitter } from '@angular/core';
import { CommonSettingsService } from '/services/common-settings.service';
import { CollapsibleTreeComponent } from '/collapsible-tree/collapsible-tree.component';
@Component({
selector: 'treeview-result',
templateUrl: './side-resultview.component.html',
styleUrls: ['./side-resultview.component.css']
})
export class SideResultviewComponent implements OnInit {
@Output()
public getNodeTokenValueFromTree = new EventEmitter<any>();
@ViewChild(CollapsibleTreeComponent, { static: false })
nodeTokenValue = 0;
searchResultJson: any[];
constructor(public commonSettingsService: CommonSettingsService) {
this.searchResultJson = [];
}
ngOnInit(): void {
this.commonSettingsService.getElasticResult().subscribe(data => {
this.searchResultJson = data.results;
});
}
public getNodeTokenValue($event: any): void {
this.nodeTokenValue = $event;
this.getNodeTokenValueFromTree.emit(this.nodeTokenValue);
}
}
In app-component.html:
<div id="grouping" class="tab-pane fade active">
<treeview-result (getNodeTokenValueFromTree)="checkNodeItemId($event)"></treeview-result>
</div>
In app-component.ts:
import { Component, OnInit, ViewChild } from '@angular/core';
import { SideResultviewComponent } from '/side-resultview/side-resultview.component';
@Component({
selector: 'app-compoment',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
@ViewChild(SideResultviewComponent, { static: false })
tabIndex: any;
constructor() { }
ngOnInit() { }
checkNodeItemId($event: any) {
this.tabIndex = parseInt($event);
console.log(this.tabIndex); // Giving console out for first-level Node only not the next level.
}
}