3

I have used the component nested inside the same component. when I change the checkbox in the parent component attached function called correctly and Event emitter is working fine but when I change the child checkbox attached function was fired but event emitter not available (as of my knowledge ever matter is not bind with child one). I want to emit the data when I change the checkbox in the child.

If anyone know the answer please help me to solve this problem. And what is the terminology for using component nested inside the same component?

Thanking you

This is stackblitz link https://stackblitz.com/edit/angular-material-normal-tree (please check the console)

tree.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-tree',
  templateUrl: './tree.component.html',
  styleUrls: ['./tree.component.css'],
})

export class TreeComponent {
  @Input() treeData: any;
  @Output() toggleEmitter: EventEmitter<any> = new EventEmitter();

  constructor() {}

  toggleChild(node) {
    console.log('from tree start');
    console.log(node);
    console.log('from tree end');
    this.toggleEmitter.emit(node);
    // if ('children' in node && node.children.length) {
    //   node.isExpand = !node.isExpand;
    // }
  }

  test(node, i) {
    node.parentIndex = i;
    console.log(node);
    // this.toggleEmitter.emit(node);
  }

  CBchangeEvent(node) {
    console.log(node);
    this.toggleEmitter.emit(node);
  }

}

tree.component.html

<ul *ngIf="treeData">
    <li *ngFor="let node of treeData;let i = index;">
        <button mat-icon-button="" mattreenodetoggle="" class="mat-icon-button" (click)="toggleChild(node)">
        <span class="mat-button-wrapper">
          <mat-icon class="mat-icon material-icons" role="img" aria-hidden="true"> 
            {{node.isExpand != 0 ? 'expand_more' : 'chevron_right'}} 
          </mat-icon>
        </span>
      </button>
        <mat-checkbox class="checklist-leaf-node" (change)="CBchangeEvent(node)">{{node.name}}</mat-checkbox>
        <app-tree *ngIf="node.isExpand" [treeData]="node.children" (toggleEmitter)="test(node,i)"></app-tree>
    </li>
</ul>

tree.component.css

ul {
    list-style-type: none !important;
}

tree.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TreeComponent } from './tree.component';
import {
  MatIconModule, MatCheckboxModule, MatFormFieldModule, MatButtonModule
} from '@angular/material';


@NgModule({
  imports: [
    CommonModule,
    MatIconModule, 
    MatCheckboxModule, 
    MatFormFieldModule, 
    MatButtonModule
  ],
  declarations: [TreeComponent],
  exports: [TreeComponent]
})
export class TreeModule {
  static forRoot() {
    return {
      ngModule: TreeModule
    }
  }
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  myData = [
    {'id':1, 'name':'Main 1','isExpand':false,
     'children':[
       {'id':1, 'name':'Child 1', 'isExpand':false,
       'children':[
         {'id':2, 'name':'Test2','isExpand':false}
       ]
       }
     ] 
    },
    {
      'id':2, 'name':'Main 2','isExpand':false
    }
  ]

  test(node) {
    console.log('from app start');
    console.log(node);
    console.log('from app end');
    if ('children' in node && node.children.length) {
      node.isExpand = !node.isExpand;
    }
  }
}

app.component.html

<app-tree [treeData]='myData' (toggleEmitter)="test($event)"></app-tree>

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TreeModule } from './tree/tree.module';
import {
  MdToolbarModule,
  MdTabsModule,
  MdButtonModule,
  MdInputModule,
  MdDatepickerModule,
  MdNativeDateModule,
  MdCheckboxModule,
  MdRadioModule,
  NoConflictStyleCompatibilityMode
} from '@angular/material';

import { AppComponent } from './app.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule, BrowserAnimationsModule, MdToolbarModule, MdTabsModule, MdButtonModule, MdInputModule, MdDatepickerModule, MdNativeDateModule, MdCheckboxModule, MdRadioModule, TreeModule, NoConflictStyleCompatibilityMode ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
cfprabhu
  • 5,267
  • 2
  • 21
  • 30

2 Answers2

0

Event Emitter works in this way

Parent passes Data to child

and

Child Emits when to let know parent that Data has been - so that parent can call its call back function

For child Knowing Parent has changed Date

Here there is a data binding happening, just like how *ngif works when ever child check data it will always be latest data.

child component Input() variable will be in same state of variable which is passed on binding. Binding takes care of updating child UI - on change in state of binded varaibles

Akhil Surapuram
  • 654
  • 1
  • 8
  • 22
  • Yes, brother. I know the working concept. If you know any alternative way to achieve my goal let me know, please. – cfprabhu Jun 02 '20 at 19:05
0

instead of biding "test" you should bind "toggleChild" in child component.

change

<app-tree *ngIf="node.isExpand" [treeData]="node.children" (toggleEmitter)="test(node,i)"></app-tree>

to

<app-tree *ngIf="node.isExpand" [treeData]="node.children" (toggleEmitter)="toggleChild(node,i)"></app-tree>
Muhammad Usman
  • 101
  • 1
  • 11
  • No brother. it's wrong. Read my question again and check the checkbox – cfprabhu Jun 02 '20 at 19:17
  • sorry, mybad. so what you want is that when child node (e.g "child1" in your data) is toggled, event should be emitted with its value ? – Muhammad Usman Jun 02 '20 at 19:50
  • https://stackoverflow.com/questions/62093782/angular-8-component-nested-inside-the-same-component-event-emitter#comment109936129_62093782 – cfprabhu Jun 02 '20 at 19:52
  • in child you are binding the method with outer node value. (toggleEmitter)="test(node,i)" can't you use $event here to get child node's value. something like (toggleEmitter)="test($event,i)" or test($event,node,i)? – Muhammad Usman Jun 02 '20 at 19:55
  • i uncommented this line in test this.toggleEmitter.emit(node); // line 30 and change the value from (toggleEmitter)="test(node,i)" to (toggleEmitter)="test($event,i)" and was able to get the child node's value all the way till app component. if you don't want event to be emitted recursively then you can just leave that commented and do whatever you want to do with the child node value in test. – Muhammad Usman Jun 02 '20 at 20:07