Imagine that you have a component with a nested structure. You’ve found a case where one of the children needs to run some functionality on one of its closest parents.
To demonstrate this, let’s assume we have the following structure:
<div> <div class="widget-content"> <div> <h1>...</h1> <div> <widget-item></widget-item> </div> </div> </div> </div>
Let’s say the widget-item
element needs to add a specific class to the closest parent, which matches the widget-content
class selector when a specific condition is true.
Your initial instinct will probably be to use the method we all know from JQuery — the closet()
method.
For example:
@Component{...} class WidgetItemComponent { constructor(private _host: ElementRef) { }ngAfterViewinit() { if(condition) { const closetParent = $(this._host.nativeElement).closest('widget-content'); $(closetParent).addClass('overflow'); } } }
The above code will work, but as professional Angular developers, we need to use the tools Angular provides us with to achieve the same result. Let’s see how we can do this.
First, we need to create a directive whose selector will match the required parent element. I like to call these ‘Query Directives’.
import { Directive, HostBinding } from '@angular/core';@Directive({ selector: '.widget-content' }) export class WidgetContentDirective {
}
The next thing we need to do is to expose an API to add our custom class to the element.
@Directive({ selector: '.widget-content' }) export class WidgetContentDirective { @HostBinding('class.overflow') private _addOverflow = false;addOverflow() { this._addOverflow = true; }
}
Here, we are using the HostBinding
decorator to add the overflow
class to our host component.
Now, we can obtain a reference to our directive inside the widgetItem
component, using the Host
decorator.
Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component.
and call the addOverflow()
method when necessary.
@Component({ selector: 'widget-item' ... }) export class WidgetItemComponent implements OnInit { @Input() condition;constructor(@Host() private _widgetContentQuery: WidgetContentDirective) { }
ngOnInit() { if(this.condition) { this._widgetContentQuery.addOverflow(); } }
}