Querying For The Closest Parent Element in Angular


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 widgetItemcomponent, 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(); } }

}




0 comments