How to emit an event from grandchildren to grandparent in modern angular?

10,910

Solution 1

There could be 2 ways:

  1. Using @output:

Grandparent

<parent (notifyGrandParent)="grandmaHandleClick($event)">
<parent>
...
grandmaHandleClick(event) {
  console.log('grandma knows you clicked')
}

Parent:

<child (handleClick)="childEvent($event)">
</child>

@Output() notifyGrandParent= new EventEmitter();
childEvent(event) {
  this.notifyGrandParent.emit('event')
}

Child is implemented properly in the code so it is good to go.

  1. Using BehaviorSubject via Service: With this much level of nesting, you can actually create some service like EventService, and then create BehaviorSubject which can directly be subscribed by the GrandParent. Also, to make this service more component specific, you can keep this service in a module which will have other 3 components (GrandParent, Parent and Child)
export class EventService{

 private childClickedEvent = new BehaviorSubject<string>('');

  emitChildEvent(msg: string){
     this.childClickedEvent.next(msg)
  }

  childEventListner(){
     return this.childClickedEvent.asObservable();
   } 

}

and then in components:

ChildComponent

export class ChildComponent{
   constructor(private evtSvc: EventService){}

   onClick(){
     this.evtSvc.emitChildEvent('clicked a button')
   }
}

GrandParent

export class GrandComponent{
   constructor(private evtSvc: EventService){}

   ngOnInit(){
     this.evtSvc.childEventListner().subscribe(info =>{
         console.log(info); // here you get the message from Child component
      })
   }
}

Please note that, with @output event, you create a tight coupling of components and so a strong dependency (parent-child-grandchild) is created. If the component is not reusable and is only created to serve this purpose, then @output will also make sense because it'll convey the message to any new developer that they have parent-child relationship.

Creating a service to pass data also exposes the data to other components which can inject service in constructor.

So, the decision should be taken accordingly.

Solution 2

Use rxjs/subject, it can be observer and observable in the same time.

Usage:

  1. Create Subject property in service:
import { Subject } from 'rxjs';

export class AuthService {
  loginAccures: Subject<boolean> = new Subject<boolean>();
}
  1. When event happens in child page/component use:
logout() {
  this.authService.loginAccures.next(false);
}
  1. And subscribe to subject in parent page/component:
constructor(private authService: AuthService) {
  this.authService.loginAccures.subscribe((isLoggedIn: boolean) => {
    this.isLoggedIn = isLoggedIn;
  })
}
Share:
10,910

Related videos on Youtube

user2167582
Author by

user2167582

Updated on October 03, 2022

Comments

  • user2167582
    user2167582 over 1 year

    If I have multiple levels of angular components, how can I use @Output to emit an event from child to the grand parent?

    Grandparent:

    <parent (handleClick)="grandmaHandleClick($event)">
    <parent>
    ...
    grandmaHandleClick(event) {
      console.log('grandma knows you clicked')
    }
    

    Parent:

    <child (handleClick)="handleClick($event)">
    </child>
    

    Child:

    <div (click)="onClick()">Click button
    </div>
    ...
    @Output() handleClick = new EventEmitter
    onClick() {
      this.handleClick.emit('clicked a button')
    }
    

    I am trying to have it so that @Output can prop drill a few components deep, whats the best way to accomplish this, and can you provide example?

  • user2167582
    user2167582 almost 5 years
    In the first approach, wouldn' @output be on the child component?
  • Shashank Vivek
    Shashank Vivek almost 5 years
    @user2167582: It has already been handled by you properly using @Output() handleClick in child component that is why I didnt mention it. Let me add that as well
  • user2167582
    user2167582 almost 5 years
    Thank you, I did and it works. upon asking this I was also curious if there's a way to pass (boundedEvent) down components without necessarily having to add handler methods to emit back up the hierarchy, seems like it is needed.
  • Shashank Vivek
    Shashank Vivek almost 5 years
    @user2167582: If its just one level of parent child components, you can use @Input and use ngOnChange in child to monitor the change in value which is passed to child component. Else, if its is multi level component structure, you can again use BehaviorSubject
  • David Silva
    David Silva over 3 years
    This was so helpful to me. Just a tiny detail.... In the original post there's the line... <div (click)="onClick">Click button</div>. The onClick should be onClick(). The answer says, "Child is implemented properly in the code so it is good to go" -- so someone might end up confused.
  • Shashank Vivek
    Shashank Vivek over 3 years
    @DavidSilva: You are correct, my friend! I'll correct that in the question. Many Thanks ! :)