JavaScript UI Libraries & Blazor Components Suite – Smart UI › Forums › Tabs › Add angular component to tab
Tagged: Angular, Angular component, Angular Tabs, dynamic component, smart-tabs, Tabs
- This topic has 9 replies, 2 voices, and was last updated 1 year, 9 months ago by dilbert.
-
AuthorPosts
-
September 16, 2019 at 6:34 am #100334adminKeymaster
how can i add an angular comonent dynamically as a tab page
September 17, 2019 at 5:52 am #100342adminKeymasterHello helkatz,
We prepared a help topic that describes how to add an Angular component dynamically to a tab item. You can find it here: https://www.htmlelements.com/docs/angular-dynamic-component-loading/. We hope it is helpful to you.
Best regards,
Dimitar
Smart HTML Elements Team
https://www.htmlelements.comFebruary 13, 2023 at 1:20 pm #104386dilbertParticipantThe mentioned help topic does work, however, as of Angular 15 the ComponentFactoryResolver is deprecated. Is there a newer method? I’m hesitant to base new software off of something that could be removed in the future.
February 13, 2023 at 2:15 pm #104387adminKeymasterAs we currently do not have this updated on our website, you may look at https://stackoverflow.com/questions/70946038/replace-deprecated-angular-componentfactoryresolver-componentfactory.
February 13, 2023 at 5:58 pm #104388dilbertParticipantWith that method I need the ViewContainerRef for the containing component which I can easily do by adding a custom anchor to smart-tab-item to expose it. However, we a new tab is added via the built in add button, the new smart-tab-item doesn’t have this tag. It also doesn’t get added to a @ViewChildren QueryList. (i.e. @ViewChildren(TabItemComponent) items: QueryList<TabItemComponent>). The items list is never updated with any smart-tab-items created using the add tab button.
Is there an easy way to get ViewContainerRef for the dynamically added smart items?
February 14, 2023 at 11:47 am #104391adminKeymasterHi dilbert,
Could you post an example about this?
Regards,
PeterFebruary 14, 2023 at 4:11 pm #104393dilbertParticipantHere is an example that illustrates the issue. The first 2 tabs get a dynamic component, but those that are added later do not.
Template:
<div>Num of smart-tab-items: {{smartItems.length}}</div> <div>Num of tabs: {{numTabs}}</div> <smart-tabs #tabs id="tabs" class="demoTabsShort" [closeButtons]="true" [closeButtonMode]="'selected'" [addNewTab]="true" (addNewTabClick)="onAddNewTabClick($event)" [reorder]="true"> <smart-tab-item [label]="'Tab_1'"><ng-container app-view-ref></ng-container></smart-tab-item> <smart-tab-item [label]="'Tab_2'"><ng-container app-view-ref></ng-container></smart-tab-item> </smart-tabs>
Main component:
import { Component, AfterViewInit, ViewChildren, QueryList, ViewChild } from '@angular/core'; import { TabItemComponent, TabsComponent } from 'smart-webcomponents-angular/tabs'; import { ThingComponent } from './thing.component'; import { ViewRefAnchorDirective } from './view-ref-anchor.directive'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit { // I am expecting this list to be updated when new tabs are added. @ViewChildren(TabItemComponent) smartItems = new QueryList(); @ViewChildren(ViewRefAnchorDirective) anchors = new QueryList(); @ViewChild('tabs', {read: TabsComponent, static: false}) tabs!: TabsComponent; numTabs = 0; ngAfterViewInit(): void { this.tabs.getTabs().then(tabs => { this.numTabs = tabs.length; }); this.anchors.forEach(tab => this.loadComponent(tab)); } onAddNewTabClick(event: Event) { this.tabs.getTabs().then(tabs => { this.numTabs = tabs.length; // At this point this.smartItems only has the original 2 smart-tab-items // It would appear the new tabs are not known to Angular since they aren't // added to the QueryList. // this.anchors also only has the original 2. this.anchors.last references // the second tab. Additional tabs do not get a new component. this.loadComponent(this.anchors.last); }); } loadComponent(viewRefAnchor: ViewRefAnchorDirective) { viewRefAnchor.viewContainerRef.clear(); viewRefAnchor.viewContainerRef.createComponent(ThingComponent); } }
Anchor directive:
import { Directive, ViewContainerRef } from "@angular/core"; /** * An directive to expose the ViewContainerRef to allow components to be dynamically * created and added. */ @Directive({ selector: '[app-view-ref]' }) export class ViewRefAnchorDirective { constructor(public viewContainerRef: ViewContainerRef) { } }
Dynamic component:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-thing', template: '<p>test-component works!</p>' }) export class ThingComponent implements OnInit { constructor() { } ngOnInit(): void { } }
February 14, 2023 at 4:16 pm #104394dilbertParticipantThe other thing I’ve tried is to use an ngFor to define the tabs:
<smart-tabs #tabs id="tabs" class="demoTabsShort" [closeButtons]="true" [closeButtonMode]="'selected'" [addNewTab]="true" (addNewTabClick)="onAddNewTabClick($event)" [reorder]="true"> <smart-tab-item *ngFor="let tab of tabNames" [label]="tab"><app-thing></app-thing></smart-tab-item> </smart-tabs>
Where tabNames is just a list of strings.
When a new tab is added (by appending a new string to the tabNames array), the content of the new tab is the new component. However, the label is not updated and there is an error in the console:
core.mjs:8506 ERROR TypeError: Cannot read properties of undefined (reading 'firstElementChild') at BaseElement._updateTabLabel (smart.tabs.js:34:65344) at BaseElement.propertyChangedHandler (smart.tabs.js:34:1110) at t.updateProperty (smart.button.js:34:75016) at BaseElement.set [as label] (smart.button.js:34:76150) at set label [as label] (smart-webcomponents-…ular-tabs.js:771:54) at Object.ngOnChangesSetInput [as setInput] (core.mjs:1586:26) at setInputsForProperty (core.mjs:11738:17) at elementPropertyInternal (core.mjs:10874:9) at Module.ɵɵproperty (core.mjs:13637:9) at AppComponent_smart_tab_item_6_Template (app.component.html:5:50)
- This reply was modified 1 year, 9 months ago by dilbert.
February 14, 2023 at 5:17 pm #104396adminKeymasterHi,
The thing is that the add new tab button adds an empty tab. It does not add a new tab with the template structure you added in the template i.e the new tab does not have ng-container in it. For that purpose we can use ng-template. Please, take a look at the updated code below
import { Component, AfterViewInit, ViewChildren, QueryList, ViewChild, ComponentRef, ChangeDetectorRef, ViewContainerRef } from '@angular/core'; import { TabItem, TabItemComponent, TabsComponent } from 'smart-webcomponents-angular/tabs'; import { ThingComponent } from './thing.component'; import { ViewRefAnchorDirective } from './view-ref-anchor.directive'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit { // I am expecting this list to be updated when new tabs are added. smartItems: TabItem[] = []; @ViewChildren(ViewRefAnchorDirective) anchors = new QueryList(); @ViewChild('tabs', {read: TabsComponent, static: false}) tabs!: TabsComponent; @ViewChild('dynamic', { read: ViewContainerRef }) viewRef!: ViewContainerRef; numTabs = 0; ngAfterViewInit(): void { this.tabs.getTabs().then(tabs => { this.numTabs = tabs.length; }); this.anchors.forEach(tab => this.loadComponent(tab as any)); this.smartItems = Array.from(document.querySelectorAll('smart-tab-item')); } onAddNewTabClick(event: Event) { this.tabs.getTabs().then(tabs => { this.numTabs = tabs.length; // create a new dynamic component. const container = document.createElement('div'); const componentRef: ComponentRef<ThingComponent> = this.viewRef.createComponent(ThingComponent); // dynamically add the component to a new container host element. container.appendChild(componentRef.location.nativeElement); // update tabs. this.tabs.update(tabs.length-1, 'Updated Tab', container); this.smartItems = Array.from(document.querySelectorAll('smart-tab-item')); }); } loadComponent(viewRefAnchor: ViewRefAnchorDirective) { viewRefAnchor.viewContainerRef.clear(); return viewRefAnchor.viewContainerRef.createComponent(ThingComponent); } }
Hope this helps.
Regards,
PeterFebruary 14, 2023 at 5:55 pm #104397dilbertParticipantThat works. Thanks!
-
AuthorPosts
- You must be logged in to reply to this topic.