I want to show Parent Children graphical representation on Angular Material Flat Tree.
This is the design :
Here is the DEMO what i done so far.
FIRST SOLUTION
here is the stackBlitz Demo
here is the stackBlitz editable link
I've made a recursive call on every visible node reffering to the node index using treeControl.dataNodes.indexOf(node)
here is the recursive function:
recNode( arr:any[], data:any[], index:number, maxIndex:number ):any[] {
if ( arr === undefined )
arr = [];
for ( let i = 0; i < data.length; i++ ) {
index++
if ( index === maxIndex ) return ( [ true, index, arr ] );
if ( data[i].children.length ) {
let res = this.recNode( arr, data[i].children, index, maxIndex )
index = res[1];
if ( res[0] === true ) {
arr.splice( 0, 0, ( i !== ( data.length - 1 ) ) )
return ( [ true, index, arr ] );
}
}
}
return ( [ false, index, arr ] );
}
this returns an array of true
false
values for your [ngClass]
then in html I call that function as so
<li *ngFor="let r of recNode( undefined, dataSource.data, -1, treeControl.dataNodes.indexOf( node ) )[2]; [ngClass]="{'node-arrow': r, 'node-arrow-empty': !r}" ></li>
and finally I've created another class in the css called node-arrow-empty
without borders
.node-arrow-nox {
position: relative;
// min-width: 48px;
// min-height: 48px;
.node-arrow {
position: relative;
min-width: 48px;
min-height: 48px;
&:before {
content: "";
position: absolute;
top: -20px;
left: 20px;
border-left: 1px solid $copounsCount;
bottom: 0;
}
}
.node-arrow-empty {
position: relative;
min-width: 48px;
min-height: 48px;
&:before {
content: "";
position: absolute;
top: -20px;
left: 20px;
bottom: 0;
}
}
}
depending on weither the value in the array is true
or false
it switches in the html between the two classes.
SECOND SOLUTION
stackBlitz DEMO
stackBlitz editable link
the other solution would be to keep track of last element in array by adding for example a isLast:boolean
property. Of course if the datas you are dealing with are dynamic you need to find a way to dinamically change that value.
Once the isLast
is filled you make a recursive call which will find the previous sibling with current node level - 1
. That being said you will check 2 things. First thing is, is the node classes contains n-last
(which is the class name I gave to Html representing last element of parent array) and second thing retrieve the class n-{{node-level}}
.
By recursively (you can make a non recursive method if you wan't to) retrieving the previousElementSibling
which has class n-3
or n-2
... to n-0
you can check if each of this element contains a n-last
class. You push the true
or false
into an array and just as the first solution switch between class node-arrow
and node-arrow-empty
.
here is the recursive function
dummy( el:any, arr:any[], level:number ) {
let classes = el.className;
if ( arr === undefined )
arr = [];
else {
arr.splice( 0, 0, classes.includes( 'n-last' ) );
}
let np = /(?!n-)\d+/g;
let m = classes.match( np );
let nLevel = parseInt( m[0] );
nLevel--;
if ( nLevel < 0 ) return ( arr );
while ( parseInt( el.className.match( np )[0] ) !== nLevel ) {
el = el.previousElementSibling;
}
arr = this.dummy(el, arr, nLevel);
return (arr);
}
the HTML (here is only parent node but same thing for child)
<mat-tree-node #refP class="parent-node {{'n-' + node.level}} " *matTreeNodeDef="let node; when: grpNode;" matTreeNodePadding matTreeNodePaddingIndent="0" cdkDrag [cdkDragData]="node" [ngClass]="{'no-child': !node.children.length, 'n-last': node.isLast}">
<span class="node-arrow-nox d-flex">
<li [ngClass]="{'node-arrow': !r , 'node-arrow-empty': r}" *ngFor="let r of dummy(refP, undefined, node.level)"></li>
</span>
<button class="node-toggle" mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.filename" [disabled]="!node.children.length">
<mat-icon class="mat-icon-rtl-mirror toggle-arrow">
{{treeControl.isExpanded(node) ? 'play_arrow' : 'play_arrow'}}
</mat-icon>
</button>
<div class="node-icon icon-h-arw"></div>
<div class="node-name">{{node.isLast}}-{{node.accntCode}} <span>: :</span> {{node.name}} </div>
</mat-tree-node>
as you can see I'm passing element to the dummy fucntion with a template variable called #refP
and here are the PARENTNODE
and CHILDNODE
declaration:
export class PARENTNODE {
id: string;
isLast: boolean;
...
actAsSupplier: boolean;
}
/* Flat node with expandable and level information */
export class CHILDNODE {
constructor(
public id: string,
public isLast: boolean,
...
public actAsSupplier: boolean,
){}
}
THIRD SOLUTION
I won't code it but I'll try to explain it.
This is kind of similar to the previous solution except your could parse when tree is changed (swapping, deletion, add) the data and add a property like divType
populated with true
or false
and call it in your html as so
<li *ngFor="let r of node.divType" [ngClass]="{'node-arrow': r, 'node-arrow-empty': !r}" ></li>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With