首页 > 解决方案 > Mat-tab-group's [(selectedIndex)]=... binding is flaky

问题描述

I am using Angular and the MaterialTabs module.

I have a list of tabs in my component that I display using mat-tabs and ngFor, plus one more disabled tab with a plus button. When clicking the plus button, I would like to add a new tab and immediately focus on that tab. (Similarly to how browser tabs work.)

This is how it looks like:

Example

I do this by two-way property-binding to the selectedIndex property of the mat-tab-group and setting it from my component inside the button click event handler.

You can see that the tabgroup's property and the property bound to it is equal, when it works.

However, I ran into a problem, where if I freshly load the page and click on the button first, the property binding somehow breaks:

Issue

Clicking once on any of the Tabs seems to fix the issue forever. The issue comes back when you reload the page.

From my understanding the following property binding would make sure that the values will always be equal and if one of them changes the other will follow:

[(selectedIndex)]="selectedIndexBinding"

So how can selectedIndex be 1, while selectedIndexBinding is 0?

How can I fix this issue?

Live example:

https://stackblitz.com/edit/angular-82vgrj

Code:

src/app/app.component.html

<mat-tab-group [(selectedIndex)]="selectedIndexBinding" #tabGroup>
  <mat-tab *ngFor="let tab of tabs">
    <ng-template mat-tab-label>
      {{ tab }}
    </ng-template>
  </mat-tab>
  <mat-tab disabled>
      <ng-template mat-tab-label>
        <button mat-icon-button (click)="addTab()">
          +
        </button>
      </ng-template>
  </mat-tab>
</mat-tab-group>

selectedIndex: {{ tabGroup.selectedIndex }}<br />
selectedIndexBinding: {{ selectedIndexBinding }}

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public selectedIndexBinding = 0;
  public tabs = [];

  public addTab() {
    this.tabs.push("Tab");
    this.selectedIndexBinding = this.tabs.length - 1;
  }
}

标签: angularangular-materialmat-tab

解决方案


您的问题来自您设置新索引的方式,因为无论如何buttona都很重要tab

您需要使用以下方法检索原件mat-tab-group

@ViewChild('tabGroup', {static: false}) tab: MatTabGroup;

并直接设置您想要的索引:

this.tab.selectedIndex = this.tabs.length - 1;

你可能会注意到我把它都放在了一个setTimeout. 它用于Angular 生命周期钩子,setTimeout 触发一个新的 Angular 周期。

这是您的工作 StackBlitz

代码:

src/app/app.component.html

<mat-tab-group [(selectedIndex)]="selectedIndexBinding" #tabGroup>
  <mat-tab *ngFor="let tab of tabs">
    <ng-template mat-tab-label>
      {{ tab }}
    </ng-template>
  </mat-tab>
  <mat-tab disabled>
      <ng-template mat-tab-label>
        <button mat-icon-button (click)="addTab($event)">
          +
        </button>
      </ng-template>
  </mat-tab>
</mat-tab-group>

selectedIndex: {{ tabGroup.selectedIndex }}<br />
selectedIndexBinding: {{ selectedIndexBinding }}

src/app/app.component.ts

import { Component, ViewChild } from '@angular/core';
import { MatTabGroup } from '@angular/material';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('tabGroup', {static: false}) tab: MatTabGroup;
  public selectedIndexBinding = 0;
  public tabs = [];

  public addTab(e: Event) {
    this.tabs.push("Tab");
    e.preventDefault();

    setTimeout(() => {
      console.log(this.tabs, this.tab.selectedIndex);
      this.tab.selectedIndex = this.tabs.length - 1;
    });
  }
}

推荐阅读