3
votes

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 { }
2
@BerkKurkcuoglu This is not related to my question. Thank you - cfprabhu
Hey @cfprabhu , you are using app-tree tag inside tree-component.html? tree-component.html tag again attached to app-tree component. won't that me making an infine recurring loop? - Akhil Surapuram
Oh I got its more like recursion building tree. - Akhil Surapuram
Hey @cfprabhu you have commented out emit code in below function test(node, i) { node.parentIndex = i; console.log(node); // this.toggleEmitter.emit(node); } - Akhil Surapuram

2 Answers

0
votes

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

0
votes

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>