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:
- When child is clicked it toggles the display of its details
- 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
'sElementRef
and traverse the DOM upwards looking for the closestParentComponent
'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.
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:
Contras:
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:
Contras:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With