knurd nerd
knurd nerd

Reputation: 324

Angular2 event for component appear / disappear

I have a tabbed UI using a very simple approach like this:

<div *ngIf="active" class="pane">
  <ng-content></ng-content>
</div>

Active is set to true on the component when the tab becomes selected.

There are multiple components in the tabs that all poll data from a webservice, the relevant code:

export class DebugComponent implements OnInit {
  public timerSubscription: Subscription;

  ngOnInit() {
    this.startTimer(0);
  }

  private startTimer(delay: number){
    let timer = Observable.timer(delay, 1000);
    this.timerSubscription = timer.subscribe(x => this.execute());
  }

  private stopTimer(){
    this.timerSubscription.unsubscribe();
  }

  execute() {
    this.stopTimer();
    // do stuff
    this.startTimer(5);
  }
}

While it works well to start the timer using ngOnInit, I have found no way to stop it when the component gets removed from the visible DOM.

What would be the best approach to get notified when the component gets removed from the visible DOM by the ngIf condition to stop the timer?

Thanks,

UPDATE: The whole story

Thanks to Günter, I figured out the solution to the problem. As I believe this to be a common requirement, I'll try to put all the pieces together here (not full copy and paste ready code, but all the needed parts):

A simple tab component, based on this http://blog.thoughtram.io/angular/2015/04/09/developing-a-tabs-component-in-angular-2.html:

The main tabs component containing the tabs:

tabs-component.ts

import {Component, ContentChildren, QueryList, AfterContentInit} from '@angular/core';
import {TabComponent} from './tab.component';

@Component({
  selector: 'tabs',
  template: `
    <ul class="nav nav-tabs">
      <li *ngFor="let tab of tabs" (mouseup)="selectTab(tab)" [class.active]="tab.active">
        <a href="#">{{tab.title}}</a>
      </li>
    </ul>
    <ng-content></ng-content>
  `
})
export class TabsComponent implements AfterContentInit {

  @ContentChildren(TabComponent) tabs: QueryList<TabComponent>;

  // contentChildren are set
  ngAfterContentInit() {
    // get all active tabs
    let activeTabs = this.tabs.filter((tab) => tab.active);

    // if there is no active tab set, activate the first
    if (activeTabs.length === 0) {
      this.selectTab(this.tabs.first);
    }
  }

  selectTab(selectedTab: TabComponent) {
    // deactivate active tab
    this.tabs.filter((tab) => tab.active).forEach(tab => tab.disable());

    // activate the tab the user has clicked on.
    selectedTab.activate();
  }

}

The tab component to put inside the tabs component:

tab-component.ts

import {Component, Input, ContentChildren, QueryList} from '@angular/core';
import {TabChild} from "./tab.child";

@Component({
  selector: 'tab',
  styles: [`
    .pane{
      padding: 1em;
    }
  `],
  template: `
    <div *ngIf="active" class="pane">
      <ng-content></ng-content>
    </div>
  `
})
export class TabComponent {
  @Input('tabTitle') title: string;
  @Input() active = false;

  @ContentChildren(TabChild) content:QueryList<TabChild>;

  public activate(){
    this.active = true;
    this.content.toArray().forEach(dc => dc.tabActivated());
  }

  public disable(){
    this.active = false;
    this.content.toArray().forEach(dc => dc.tabDisabled());
  }
}

The components to display inside the tabs need to inherit from this base class:

tab-child.ts

import {Directive} from "@angular/core";

@Directive({selector: "TabChild"})
export class TabChild {
  public tabActivated() : void{}
  public tabDisabled() : void{}
}

A sample component looks like this:

sample-component.ts

import {Component, ApplicationRef, forwardRef} from '@angular/core';
import {TabChild} from "./tab.child";

@Component({
  selector: 'sample',
  templateUrl: "app/sample.component.html",
  providers: [{provide: TabChild, useExisting: forwardRef(() => SampleComponent) }]
})
export class SampleComponent implements TabChild {

  tabActivated(): void {
    console.log("Sample tabActivated");
  }

  tabDisabled(): void {
    console.log("Sample tabDisabled");
  }
}

Putting it all together:

@Component({
  selector: "my-app",
  template: `<tabs>
    <tab tabTitle="Sample1">
        <sample></sample>
    </tab>
    <tab tabTitle="Sample2">
        <sample></sample>
    </tab>
  </tabs>`
})

Upvotes: 2

Views: 3822

Answers (1)

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657506

You can use the ngOnDestroy lifecycle callback

export class DebugComponent implements OnInit, OnDestroy {

   ngOnDestroy() {
     this.stopTimer();
   }

See also https://angular.io/docs/ts/latest/api/core/index/OnDestroy-class.html

Upvotes: 1

Related Questions