Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tree material angular 2 selected state

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>
like image 258
Bogdan Avatar asked May 31 '18 20:05

Bogdan


2 Answers

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!

like image 139
Jony Adamit Avatar answered Oct 27 '22 16:10

Jony Adamit


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.

like image 22
John Avatar answered Oct 27 '22 15:10

John