1
votes

I have a basic angular application with 3 components as below:

  1. car-component which is the main or parent component.
  2. carousel-component (child of car-component, *uses ng-content)
  3. child-component (child of carousel-component)

The button inside the child-component emits an event using the EventEmitter() on click. As you can see from the code in plunker, the car-component (which is the main or master component) is able to listen and capture the event emitted by its child's child component (i.e. child-component.ts) and {{text}} is updated in the car-component template.

However the carousel-component which is the immediate parent of child-component fails to capture the same event and no text is updated inside the carousel-component.

car-component.ts

import  {Component} from 'angular2/core';
import {CarouselComponent} from './carousel-component';
import {Child} from './child.component';

@Component({
    selector: 'my-app',
    template: `
              <div style="border:1px solid #CCC; padding: 5px; margin: 5px;">
                car-comp txt: {{text}}
                <carousel-component>
                    <child (changed)="onChange($event)"></child>
                </carousel-component>
              </div>
    `,
    directives : [Child, CarouselComponent]
})
export class CarComponent {

    text: string = '?';

    onChange(value) {
        console.log('car-component onChange fn..');
       this.text = value;
    }

}

carousel-component.ts

import  {Component, EventEmitter} from 'angular2/core';
import {Output} from 'angular2/core';

@Component({
    selector: 'carousel-component',
    template: `
                <div style="background: #CCC; padding: 10px;">
                <div>carousel-component txt: {{carouselTxt}}</div>
                <ng-content></ng-content>
                </div>
             `,
})
export class CarouselComponent {

    carouselTxt: string = '?';

    changed = new EventEmitter();

    onChange(value) {
        console.log('carousel-comp onChange called..');
        this.carouselTxt = value;
    }

}

child.component.ts

import  {Component, EventEmitter} from 'angular2/core';
import {Output} from 'angular2/core';

@Component({
    selector: 'child',
    template: `
                <div style="background: #FFF; padding: 10px;">
                child-comp txt: 
                <input type ="text" #textInput value="{{txt}}">
                <button (click)="onChange(textInput.value)">Emit</button>
                </div>
             `,
    outputs:['changed']
})
export class Child {

    txt: string = 'abc';

    changed = new EventEmitter();

    onChange(value) {
        this.txt = value;
        this.changed.emit(value);
    }

}

Can someone please let me know where am i going wrong here?

https://plnkr.co/edit/h9mjlo?p=preview

3
Can some one please help me solve this issue? I am using Subject (from rxjs) and a service class (@injectable) as a work around for now. Would appreciate any advice or solution to the above problem anyways. Thanks.user3329460

3 Answers

0
votes

In your car-component you are listening for the child's event changed and binding it to onChange. Since this is in your car-component template it only binds it to car-component's onChange method, not carousel-component's.

To make the child-component's change event visible to all of it's hierarchical parents, create a service that all of the parents subscribe to. A guide on how to do this can be found here: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

0
votes

As mentioned in the last answer by @Ricardo car-component is having the selctor for child -component. To listen to the event in carousel-component remove child component from car-component template and add it to the carousel-component template. And you can have one more event emitter in the carousel-component which will reach the parent that is car-component. that Event you can fire from withn the fnction onChange()

0
votes

Made use of ContentChildren and ngAfterContentInit in CarouselComponent without the need for adding a service 'or' moving the template out of car-component template.

export class CarouselComponent {

    carouselTxt: string = '?';

    @ContentChildren(Child) childItems:QueryList<Child> = new QueryList<Child>();

    ngAfterContentInit () {
      this.childItems.toArray().forEach((item, n)=>{
        item.changed.subscribe(
          (val)=> { this.carouselTxt = val; },
          (e)=> { console.log('e::', e); },
          (c)=>{ console.log('c::', c); }
        );
      });
    }

}

Solution: https://plnkr.co/edit/x2mO0r?p=preview