Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Material Tabs Wrapper - Adding new tab doesn't reflect in UI

I'm trying to create a wrapper around MatTabs that exposes all the properties of the MatTabs and Came across this question : Angular Material Tabs not working with wrapper component . I added other tabs properties and events to it. The events seems to be triggering properly.

The problem comes when I add a new tab.The array is getting updated but the UI remains same.

HTML

<div>
  <span class="example-input-label"> Selected tab index: </span>
  <mat-form-field>
    <input matInput type="number" [formControl]="selected">
  </mat-form-field>
</div>

<div>
  <button mat-raised-button
          class="example-add-tab-button"
          (click)="addTab(selectAfterAdding.checked)">
    ADD NEW TAB
    <br/>
  </button>
  <mat-checkbox #selectAfterAdding> Select tab after adding </mat-checkbox>
</div>

<custom-tabs [selectedIndex]="selected.value"
               (sqSelectedIndexChange)="selected.setValue($event)">
  <custom-tab *ngFor="let tab of tabs; let index = index" [label]="tab">
    Contents for {{tab}} tab
    <button mat-raised-button
            class="example-delete-tab-button"
            [disabled]="tabs.length === 1"
            (click)="removeTab(index)">
      Delete Tab
      <br/>

    </button>
  </custom-tab>
</custom-tabs>

TS

tabs = ['First', 'Second', 'Third'];
  selected = new FormControl(0);

  addTab(selectAfterAdding: boolean) {
    this.tabs.push('New');
    console.log(this.tabs);
    if (selectAfterAdding) {
      this.selected.setValue(this.tabs.length - 1);
    }
  }

  removeTab(index: number) {
    this.tabs.splice(index, 1);
  }

Custom Tabs StackBlitz.

MatTabs Stackblitz

Ideally I'm looking to expose all the features that MatTabs provides out of the box and then some.

like image 349
kal93 Avatar asked Nov 07 '22 02:11

kal93


1 Answers

So, the reason why the tabs aren't loading is because of the code in the custom-tabs component's ngViewAfterInit() function. It was initialized, but that's it. So the this.tabGroup never gets updated with the current tabs and thus wasn't being reflected in the UI. So I added a subscription to watch for when the tabs change, and use the same logic (which I am not sure is 100% correct, but it gets the job done).

  public ngAfterViewInit() {
    const matTabsFromQueryList = this.tabs.map((tab) => tab.matTab);
    const list = new QueryList<MatTab>();
    list.reset([matTabsFromQueryList]);
    this.tabGroup._tabs = list;
    this.tabGroup.ngAfterContentInit();

   // watches for when the tabs change
    this.tabs.changes.subscribe(value => {
      const matTabsFromQueryList = value.map((tab) => tab.matTab);
      const list = new QueryList<MatTab>();
      list.reset([matTabsFromQueryList]);
      this.tabGroup._tabs = list;
      this.tabGroup.ngAfterContentInit();
      });
  }

This (again) isn't the most elegant solution because you need to interact with the tabs before they load, but it preserves the context (as demonstrated by the input box). The tabs wouldn't update without selecting a different tab, so I had to do a hacky solution where I switch this.selected.value to -1 then switch back to the correct tab, so it's a little ugly but at least it works.

stackblitz

like image 56
rhavelka Avatar answered Nov 11 '22 13:11

rhavelka