Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get parent component's elementRef from child component

I have ParentComponent and ChildComponent. Parent component uses ng-content so it can have anything within and in any kind of DOM element hierarchy.

My child component has a specific requirement:

  1. When child is clicked it toggles the display of its details
  2. When child is placed inside ParentComponent, then when the parent is clicked, child should do the same as in 1.

Since parent can have any content it seems unreasonable to add @ContentChild to the parent, because ... well encapsulation.

So the only place where this should handled is ChildComponent. Getting to ParentComponent is pretty easy by:

// ChildComponent ctor
constructor(@Optional() parent: ParentComponent) { ... }

but this doesn't help as I need parent component's ElementRef so that I would be able to attach a click event handler to it.

How can I obtain it the Angular way?

I know I could get ChildComponent's ElementRef and traverse the DOM upwards looking for the closest ParentComponent's element, but that's likely more of a hack than the proper Angular way of doing this. I'm pretty sure there must be some better way.

like image 233
Robert Koritnik Avatar asked Feb 15 '18 21:02

Robert Koritnik


1 Answers

ATM 2 ideas come to my mind:

1) Inject the parent component into the child as an optional parameter:

For this case, the parent should expose the elementRef instance through a public member.

Pros:

  • Easy to implement

Contras:

  • Tight coupling of child to parent: What if now you have N-types of parent? Would you define N-Optional properties in the child constructor? Use a base class for all the parents? In which case you will have to use an explicit provider at component level on all the N-Parent components.

2) Share a service instance across parent and child for tracking click events

In this case, both parent and child share a common instance of a service that would more or less look like this:

export class ClickTracker {
   private readonly _clicks$ = new Subject<void>();
   readonly clicks$ = this._clicks$.asObservable();

   ngOnDestroy(){
      this._click$.complete();
   }

   click(){
      this._clicks$.next(); 
   }
}

Basically, both parent and child can emit new events through the click method. They can also subscribe to that stream by using the public clicks$ stream and run logic on events.

For the instance to be shared across the DOM hierarchy, a new instance of the ClickTracker class has to be provided at parent component level:

@Component({
  providers: [ClickTracker]
})
export class FooParent{}

The hierarchical dependency injector will take care of providing a child component contained in FooParent with the ClickTracker instance from the parent.

Pros:

  • No tight coupling between child and parent(s)

Contras:

  • Parents are now responsible of creating a new service.
like image 76
Jota.Toledo Avatar answered Oct 13 '22 23:10

Jota.Toledo