Tree material angular 2 selected state

12,997

Solution 1

I managed to develop a working example of what you are trying to achieve. The presentation is basic but it uses a layout identical to your example code. I included a link at the very bottom to my solution. It basically boils down to the below code.

component.ts

  // inside of the component class

  @ViewChildren(MatTreeNode, { read: ElementRef }) treeNodes: ElementRef[];

  hasListener: any[] = [];
  oldHighlight: ElementRef;

  updateHighlight = (newHighlight: ElementRef) => {
    this.oldHighlight && this.renderer.removeClass(this.oldHighlight.nativeElement, 'background-highlight');

    this.renderer.addClass(newHighlight.nativeElement, 'background-highlight');
    this.oldHighlight = newHighlight;
  }

  ngAfterViewChecked() {
    this.treeNodes.forEach((reference) => {
      if (!this.hasListener.includes(reference.nativeElement)) {
        console.log('* tick');

        this.renderer.listen(reference.nativeElement, 'click', () => {
          this.updateHighlight(reference);
        });
        this.renderer.listen(reference.nativeElement.children.item(0), 'click', () => {
          this.updateHighlight(reference);
        });

        this.hasListener = this.hasListener.concat([ reference.nativeElement ]);
      }
    });

    this.hasListener = this.hasListener.filter((element) => document.contains(element));
    console.log('*', this.hasListener.length);
  }

component.css

.background-highlight {
  background-color: whitesmoke;
}

I positioned most of my logic inside of the ngAfterViewInit lifecycle hook. This is so I could access the results of the @ViewChild query. The query returns references to all of the <mat-tree-node></mat-tree-node> elements in the template. The results are stored in this.treeNodes as a QueryList.

I iterate across the list. I check to see if the referenced nativeElement already has its event listeners. The event listeners trigger on mouse click. The callback updateHighlight handles the removal and addition of the background-highlight css class so that it remains unique in the DOM.

I added two event listeners targeting the <mat-tree-node></mat-tree-node> and its nested <button></button> element. Clicking both places highlights the tree node all the same.

In updateHighlight I remove the background-highlight class from wherever it was previously added (if applicable). Whatever is currently clicked gets the background-highlight class. A reference to the clicked element replaces the previous value of this.oldHighlight.

For the sake of performance, I included this.hasListener. The array stores the <mat-tree-node></mat-tree-node> elements that have already received their listeners. I can check the array to ensure that I am not needlessly overwriting listeners with each pass of ngAfterViewChecked.

The last bit of logic keeps this.hasListener from growing out of control. Any elements no longer attached to the DOM are no longer a concern.

I kept in two console.log statements because their outputs reflect that the code works beyond highlighting clicked tree nodes.

For any other questions, see the repository: https://github.com/sosmaniac-FCC/mat-tree-node-example/tree/master/src/app/components/example-one . I did import some extra utilities from @angular/core.

Of course, if I missed the mark anywhere just let me know. I will follow-up the best I can.

Solution 2

There's an easier cleaner way to do this.

All you have to do is add
(click)="activeNode = node" [ngClass]="{ 'background-highlight': activeNode === node }"
to each mat-tree-node.

Don't forget to add the activeNode variable to your component.

That's it!

Share:
12,997

Related videos on Youtube

Bogdan
Author by

Bogdan

Updated on June 29, 2022

Comments

  • Bogdan
    Bogdan almost 2 years

    I have a Angular 2 Material Tree when I click a node from the tree I need to have the selected state on that node to change the background color. I have no idea how I can do that. I didn't find nothing in documentation to help me. Here is the html code and a picture that how it should look the tree

    That is how it should look the tree when clicking on a node

           <mat-tree [dataSource]="dataSource" [treeControl]="treeControl" #matTree [ngStyle]="{ 'color': red}">
            <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding>
              <button mat-icon-button disabled></button>
              {{node.filename}}
            </mat-tree-node>
    
            <mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding >
              <button mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.filename" click="onClick()">
                <mat-icon class="mat-icon-rtl-mirror">
                  {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
                </mat-icon>
              </button>
              {{node.filename}}
            </mat-tree-node>
          </mat-tree>
    
  • d11
    d11 over 5 years
    NIce BTW - do you know - if it's possible to add branch lines ? .
  • Jony Adamit
    Jony Adamit over 5 years
    Never tried it, but a quick search gave me this
  • BluJ IT
    BluJ IT about 3 years
    Don't forget the CSS changes for the component as mentioned above in the response from @John .background-highlight { background-color: whitesmoke; }