Dynamically loaded content in Angular Material Tabs

4.8k views Asked by At

I'm trying to build a dynamic tab-system from Angular Material: Tabs and I'm running into an issue with the loading of content on subsequent tabs, where the concept only works on the first tab being loaded.

Below is the tab-group. The tabs are constructed from an array of 'tabs' sent from some service, on the group, I have a change event, which should, when changed, dynamically load a component for the active tab in the .

<mat-tab-group *ngIf="tabs.length >= 1" #shiftplanningtabgroup (selectedTabChange)="selectedTabChange($event);">
    <mat-tab *ngFor="let tab of tabs; let idx = index;" [disabled]="tab.disabled">
        <ng-template mat-tab-label>
            <div class="ava-mat-tab-label font-size-12" fxLayout="row" fxLayoutGap="12px" fxLayoutAlign="center end">
                <mat-checkbox [checked]="tab.active" [disabled]="tab.disabled" (change)="checkboxStateChange($event, idx);"></mat-checkbox>
                <label class="cursor-pointer">{{ tab.label }}</label>
            </div>
        </ng-template>
        <div class="tab-content">
            <ng-template #tabHost></ng-template>
        </div>
    </mat-tab>
</mat-tab-group>

The 'selectedTabChange' event handler:

public selectedTabChange(event: MatTabChangeEvent): void {
  this.loadTabContent(event.index);
}

The 'loadTabContent' method:

private loadTabContent(index: number): void {

  this.container.clear();

  const factory = this.componentFactoryResolver.resolveComponentFactory(AvaShiftPlanningTabContentComponent);

  const componentRef = this.container.createComponent<AvaShiftPlanningTabContentComponent>(factory);
  (componentRef.instance as AvaShiftPlanningTabContentComponent).loading = true;
  (componentRef.instance as AvaShiftPlanningTabContentComponent).tab = this.tabs[index];
  (componentRef.instance as AvaShiftPlanningTabContentComponent).tabLoadingCompleted.subscribe((value: any) => {
    this.tabLoadingComplete(value);
  });
}

And the reference to #tabHost:

@ViewChild('tabHost', { read: ViewContainerRef }) container: ViewContainerRef;

This works, only on the first tab. If I add more than one tab, all subsequent tabs show no content, however, the component is definitely being hit on tab change, just that the content for the dynamically loaded component is not rendered.

Is this a limitation on Material: Tabs or something wrong with the way that I'm trying to load the component?

1

There are 1 answers

0
Alaa M. Jaddou On BEST ANSWER

Hey I think this answer is late:

but I passed here so I thought if I gave help would be nice. this is how I do it.

         <mat-tab-group (selectedIndexChange)="selected.setValue($event)"
                       [selectedIndex]="selected.value" animationDuration="0"
                       class="h-100 w-100">
            <mat-tab *ngFor="let tab of tabs; let index = index">
                <ng-template class="w-100" mat-tab-label>
                    {{ tab.label }}
                    <mat-icon (click)="removeTab(index)" class="float-end">cancel</mat-icon>
                </ng-template>
                <ng-template #frame style="max-width: 100%;"></ng-template> // <-- notice here
            </mat-tab>
        </mat-tab-group>

The frame must be ViewChildren not ViewChild coz it has many items so you can't override the same viewContainerRef this is how you can use it.

@ViewChildren('frame', { read: ViewContainerRef }) frame: QueryList<ViewContainerRef>;

The adding will be like this:

    this.tabs.push(action);

    if (selectAfterAdding) {
        this.selected.setValue(this.tabs.length - 1);
    }

and for the content dynamic you can use the Component Factory Resolver which you can find more here.

Thanks and i hope this help anyone needed it.