16

I have a nested child component that have a output Event, I want listen this event from parent component but I dont know how, I have 4 levels:

I tried to pass the event from child 3 to child 2 and child 2 to child and to Parent, but I think that this is not the best way.

-Parent (From this I want listen the event)
--Child
----Child 2
------Child 3 (This have the Event)
4
  • 4
    In the recent NG Conf, they had a very instructional video about that. I highly suggest you to watch it ! Commented May 24, 2019 at 10:25
  • I see answer describing the recommended ways to achieve the same result, but I would be interested in a answer that describes directly how to do what is asked, even if it is not the preferred way. Commented Oct 15, 2019 at 8:48
  • The "normal" way is: in child3 put eventEmiter3, in child 2 receive event3 on a Input and emit that data through the eventEmiter2 to parent, in child1 receive data from eventEmitter2 and emit to parent through another eventEmiter1. nested eventEmiter and Inputs... Commented Oct 15, 2019 at 15:28
  • I understand that, but I do not know what is the syntax to implement it in a clean way. I knew in previous angular versions, but for angular2+, I have yet to find an example for it. Commented Oct 15, 2019 at 15:35

2 Answers 2

16

Source Dan Wahlin (ng-conf: Mastering the Subject: Communication Options in RxJS ), it's not recommanded to use OutPut when you have a component in a deeper level that has to communicate with a higher lever component , imagine you have 5 or 6 leves!!, you have to use Subject instead: you can create and Event bus through an observable service

Events here is an enum of events if you want

export enum Events{
 'payment done',
  // other events here
 }

@Injectable()
export class EventService {

 private subject$ = new Subject()

 emit(event: EmitEvent) {
    this.subject$.next(event); 
  } 

 on(event: Events, action: any): Subscription {
 return this.subject$.pipe(
  filter((e: EmitEvent) => e.name == event),
  map((e: EmitEvent) => e.value)).subscribe(action);
 }

}

so now imagine that you want to emit an event from Child3 , let's say for example after a payment is done => notify parent component

export class Child3Component implements OnInit {

  constructor(public eventservice : EventService ) {}
  pay(paymentAmount: any) {
    this.eventservice.emit(
      new EmitEvent('payment done',paymentAmount));
  }
}

now in your parent component you can call on method like this and you will get the event

 export class ParentComponent implements OnInit {
   constructor(public eventservice : EventService ) {}
   ngOnInit() {
    this.eventservice.on('payment done', (paymentAmount => console.log(paymentAmount));
   }
 }
Sign up to request clarification or add additional context in comments.

8 Comments

What happens if I have 2 events in the child3 that I want to listen them in the parent? How I have to define the ParentComponent Class ( I mean the part of this.eventservice.on having 2 events to listen to)?
you have to add another call : this.eventservice.on('payment done', (paymentAmount => console.log(paymentAmount)); this.eventservice.on('customer added', (customer=> console.log(customer));
Its not define EmitEvent, I understand that is a created class, should this class have name (EventName) and value?
What happens if (like I have a situation now) you're displaying the parent component 3 times in the view (with it's children)? Sharing an Observable in the service won't work this time.
@TJ96 - this should work if you want all instances to react the same. If they should react differently, then you need to add some identifiers to your event info class (EmitEvent class in this example), and then some processing logic in your component to only handle events meant for it. You can just do nothing with the event if you find out the current component should not handle it.
|
8

Though you can use an @Output event emitter to do this, I'd suggest that you create a shared service instead that will handle the communications since there are quite many levels of nesting.

You can do something like below, and inject the service in both of your components. One will emit the message (your nested child component), and one will listen for the messages (your top level component).

Define your service

@Injectable({
    providedIn: 'root'
})
export class CommunicationService {
    @Output() message$: EventEmitter<boolean> = new EventEmitter();

    sendMessage(message: String) {
        this.change.emit(message)
    }
}

Inject it in your components

constructor(private communicationService: CommunicationService) { }

In the component where you will send the message from

sendMessage() {
    this.communicationService.sendMessage('This is a message from deep below!');
}

And in your listener component, subscribe to the event emitter

ngOnInit() {
    this.communicationService.message$.subscribe(message => {
      console.log(message);
    });
}

1 Comment

I added the short code on how to inject the CommunicationService in your components! You'll have to adapt it to fit your need since I don't know what you're trying to send between the components!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.