Search code examples
angularangular-directivelifecycleangular-ng-if

How to trigger a directive within ngIf once ngIf finishes rendering html elements?


My problem involves the *ngif directive when once *ngif=true I want the html content to render first before my 'ModClass' directive can do its stuff.

I tried to implement ngOnInit and ngViewInit but no luck since I believe ngOnInit triggers while ngif content is being rendered or maybe earlier when the parent component is generated.

Also for ngViewInit I think I need to be subscribed to something but I don't know exactly what I need to be subscribed to. (I might be completely wrong about this will do more research)

The following code illustrates my problem along with having a stacksblitz link so you won't have to recreate this code on your machine to troubleshoot.

What the code is suppose to do is on button click it toggles a boolean variable that is used with the ngIf as so: *ngIf='trigger'

The ngIf code will display a div block with default css class 'red' but my directive will change it to the 'blue' class.

UPDATE

Turns out ngViewInit would work fine as long as I use ElementRef. Thanks for all the help.

Directive in src/app/modify-class/modify-class.directive

import { Directive, OnInit } from '@angular/core'

@Directive({
  selector: 'ModClass'
})
export class ModifyClassDirective implements OnInit {

  constructor (private htmlElement: HTMLElement) {}

  //should i try ngViewinit instead
  ngOnInit () {
    this.activateThisClass()
  }

  /*ngViewInit () {
    this.activateThisClass()
  }*/
  //this function should execute once ng-if condition is true and html is rendered
  activateThisClass () {
    console.log('i\'ve been triggered')
    const list = this.htmlElement.getElementsByClassName('red')
    list.forEach(element => element.classList.remove('red'))
    list.forEach(element => element.classList.add('blue'))
  }
}

HTML/Hello Component content found in src/app/hello.component

import { Component, Input } from '@angular/core';

@Component({
  selector: 'hello',
  template: `
<h1>Hello {{name}}!</h1>
  <button ModClass='' (click)="trigger=!trigger;"> click me </button>

<div *ngIf='trigger'> 

  <!-- This is what i want to change once it is rendered -->
  <div ModClass='' class='red'> this will/should be overwritten and turn blue </div>

</div>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
  @Input() trigger: boolean;

  constructor() {this.trigger = false}
}

linkt to Stackblitz Link


Solution

  • I don't know if you intend to use the directive as described in the question (since the red class appears to be applied only when the element doesn't exist) but here are two things that you can change to make it work:

    • Change the directive selector from ModClass to [ModClass]
    • Inject ElementRef in the directive constructor instead of HTMLElement

    Here is the modified code:

    import { Directive, ElementRef, OnInit } from '@angular/core'
    
    @Directive({
      selector: '[ModClass]'
    })
    export class ModifyClassDirective implements OnInit {
    
      constructor (private elementRef: ElementRef) {}
    
      ngOnInit () {
        this.activateThisClass()
      }
    
      activateThisClass () {
        console.log("activateThisClass triggered!")
        const element = this.elementRef.nativeElement;
        element.classList.remove('red');
        element.classList.add('blue');
      }
    }
    

    See the modified stackblitz.