Components are not being destroyed after closing a mat-tab when building a mat-tab-group from an array

3.9k Views Asked by At

I have a dataset which has an array of panels containing information about the component that should be loaded in a mat-tab-group. The tabs in the mat-tab-group can be closed. For that, I change the data structure that we use to build the tab-group. So when we have an array of 4 panels (that render 4 tabs in the tab-group) and we remove a panel, the array will only have 3 values and only three tabs are rendered.

The problem is that the components within the removed tab will actually stay alive. I tested this by adding an interval in the constructor of my component. When the component is gone, I expected it to really be gone, but the console.log in the interval will keep on logging.

Here's a stackblitz of the minimal reproduction: https://stackblitz.com/edit/angular-wfkpqq

I did some Google searches and checked the docs but I can't find anything about closing mat-tabs. There was one dude telling someone that this should be implemented by the user, but they don't really give you the tools to do this proper.

How can I make sure my orphaned components are destroyed?

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

@Component({
  selector: 'app-dock',
  templateUrl: './dock.component.html',
  styleUrls: ['./dock.component.css']
})
export class DockComponent {

  public dock: any;

  constructor() {
    this.dock = {
      panels: [
        {
          name: 'panel1'
        },
        {
          name: 'panel2'
        },
        {
          name: 'panel3'
        },
        {
          name: 'panel4'
        }
      ]
    }
  }

  public onClose(panel): void {
    var index = this.dock.panels.indexOf(panel);
    if (index > -1) {
      this.dock.panels.splice(index, 1);
    }
  }
}
<mat-tab-group #tabs>
 <ng-container *ngFor="let panel of dock.panels">
  <mat-tab>
   <ng-template mat-tab-label>
    <div>
     <span class="tab-title">{{panel.name}}</span>
     <span style="flex: 1 1 auto;"></span>
     <div class="tab-options">
      <button mat-icon-button (click)="onClose(panel)">
              <mat-icon>close</mat-icon>
            </button>
     </div>
    </div>
   </ng-template>

   <!-- ng-template with matTabContent makes the tabs lazy load -->
   <ng-template matTabContent>
    <app-annoying [name]="panel.name"></app-annoying>
   </ng-template>
  </mat-tab>
 </ng-container>
</mat-tab-group>

[edit] The problem was something else. I detached a dynamically inserted component so I could move components. I also did that when closing a component, so the panel was detached, so the OnDestroy would never be called. I will accept the only answer as it guided me to my mistake.

1

There are 1 best solutions below

2
On BEST ANSWER

I think I know what your problem is. Your components are being destroyed, the problem is that you are not closing the interval. I played arround with your stackblitz and if you add a ngOnDestroy (and implement OnDestroy) and you close the interval, you will see that everything works as expected

change your annoying.component.ts:

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

@Component({
  selector: 'app-annoying',
  templateUrl: './annoying.component.html',
  styleUrls: ['./annoying.component.css']
})

export class AnnoyingComponent implements OnDestroy {
  @Input() public name: string;
  _interval:any;
  constructor() {
    this._interval = setInterval(() => {
      console.log(this.name + ' still lives');
    }, 1000);
   }

   ngOnDestroy(){
     clearInterval(this._interval);
     console.log(this.name + "is being destroyed");
   }
}

and you will see that the log "is being destroyed" shows and the other logs stop when you close the tab.

The problems was with the lack of

clearInterval(this._interval);

Take a look at this answer:

https://stackoverflow.com/a/42395773/7041393