Manzur Khan
Manzur Khan

Reputation: 2486

Trigger event when element becomes visible with ngIf

I'm having some divs with ngIf, I just want to have a way to know if the particular div is the one which is visible/active right now like an event trigger like focus (it doesn't work) or something, and with this event, I will set a variable or something.

<div *ngIf="test === true" (focus)="myVariable = true">
</div>

Upvotes: 16

Views: 29640

Answers (4)

Cozmin
Cozmin

Reputation: 66

A solution would be to use @ViewChildren and to subscribe to the changes Observable of QueryList in ngAfterViewInit(), also to avoid ExpressionChangedAfterItHasBeenCheckedError ( this happens if for example you want to change a property that is used in the template when the div is visible) you can use detectChanges() of ChangeDetectorRef like this:

@Component({
  selector: "my-app",
  template: `
    <div *ngIf="num % 10 === 0" #doSomethingWhenVisibleDIV>
      Show some content
    </div>
    <div *ngIf="showOtherDiv">Show different content here</div>
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
  num: number = 0;
  showOtherDiv: boolean = false;

  private subscription: Subscription;

  @ViewChildren("doSomethingWhenVisibleDIV") divs: QueryList<ElementRef>;

  constructor(private changeDetectorRef: ChangeDetectorRef) {}

  ngOnInit() {
    setInterval(() => this.num++, 1000);
  }

  ngAfterViewInit() {
    this.subscription = this.divs.changes.subscribe({
      next: () => {
        if (this.divs.length > 0) {
          this.showOtherDiv = !this.showOtherDiv;
          this.changeDetectorRef.detectChanges();
        }
      }
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Stackblitz example

Upvotes: 4

Cameron Burger
Cameron Burger

Reputation: 79

I would like to build on Rachit's answer.

<div *ngIf="test"><ng-container *ngIf="runShow && show()"></ng-container></div>

and in the component

this.runShow = true;

//show always returns true.
show() {
  //Return if not running. Shouldn't be needed as runShow proceeds show in the template.
  if(!this.runShow) {
    return true;
  }
  //Do modifications...

  this.runShow = false;
  return true;

show() will only run if test is truthy, and will turn itself off after a single iteration (of course, you can change this.runShow to be based off something). An important gotcha is that until this.runShow == false, this will run every time the component detects a change, no more and no less. We put the show() inside its own ng-container so that it doesn't impact the DOM and is run after the test is rendered.

Upvotes: 2

Rachit Shroff
Rachit Shroff

Reputation: 135

This can be a possible work around. It might not be the best one but will work.

In html file,

<div *ngIf="show()"> </div>

In component TS file,

show(){
  if(test){ //condition for showing the div
    myVariable = true; 
    return true;
  }
  else
    return false;
}

Upvotes: 3

user4676340
user4676340

Reputation:

Your div will be rendered and visible once the change detection is triggered. When a change is detected, the whole lifecycle is ran again.

If you want to run something, you should hook on one of the events of the lifecycle. I suggest AfterViewInit.

Now that you know how, let's see what you should do.

In your case, you should create div with template references. This will allow you to have a reference to the element and make you able to check which div is shown or hidden.

Here is a stackblitz that shows you how it works, and here is the code :

import { Component, ViewChildren, QueryList, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
  <div *ngFor="let item of [0, 1, 2, 3, 4, 5]; let i = index">
    <span *ngIf="i === show" #shownDiv [id]="'div-' + i">{{ item }}</span>
  </div>
  `
})
export class AppComponent {
  name = 'Angular 6';
  show = 0;

  @ViewChildren('shownDiv') divs: QueryList<ElementRef>;

  ngOnInit() {
    setInterval(() => {
      this.show++;
      if (this.show > 5) {
        this.show = 0;
      }
    }, 1000);
  }

  ngAfterViewChecked() {
    let shown = this.divs.find(div => !!div);
    console.log('DIV shown is ' + (shown.nativeElement as HTMLSpanElement).id);
    // Now that you know which div is shown, you can run whatever piece of code you want
  }
}

Upvotes: 6

Related Questions