It is my app.component.ts:
import { Component } from '@angular/core';
@Component({
templateUrl: 'app/app.component.html',
selector: 'my-app'
})
export class AppComponent {
}
And this is my app.component.html:
<p-tabView>
<p-tabPanel header="Home" leftIcon="fa-bar-chart-o">
<home-app></home-app>
</p-tabPanel>
<p-tabPanel header="Hierarquia" leftIcon="fa-sitemap">
<tree-app></tree-app>
</p-tabPanel>
<p-tabPanel header="Configurações" leftIcon="fa-cog">
<config-app></config-app>
</p-tabPanel>
</p-tabView>
My three components (home, tree and config) are been loaded at the same time when the tabView is loaded. However, I would like that a component was loaded just when its tab was selected. How to do that?
P.S.: if it helps, TabView has an onChange event.
You can use SystemJsNgModuleLoader
which is used in angular2 routing
Live Plunker
First you can write component which will load module:
@Component({
selector: 'dynamic-container',
template: `
<template #container></template>
<div *ngIf="!loaded" class="loader"></div>
`,
styles: [`
.loader {
position: relative;
min-height: 100px;
}
.loader:after {
content: 'Loading module. Please waiting...';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`]
})
export class DynamicContainerComponent implements OnDestroy {
@ViewChild('container', { read: ViewContainerRef }) vcRef: ViewContainerRef;
loaded: boolean;
constructor(private moduleLoader: SystemJsNgModuleLoader) { }
compRef: ComponentRef<any>;
@Input() modulePath: string;
@Input() moduleName: string;
_inited: boolean
set inited(val: boolean) {
if(val) {
this.loadComponent();
}
this._inited = val;
};
get inited() {
return this._inited;
}
loadComponent() {
this.moduleLoader.load(`${this.modulePath}#${this.moduleName}`)
.then((moduleFactory: NgModuleFactory<any>) => {
const vcRef = this.vcRef;
const ngModuleRef = moduleFactory.create(vcRef.parentInjector);
const comp = ngModuleRef.injector.get(LazyLoadConfig).component;
const compFactory = ngModuleRef.componentFactoryResolver.resolveComponentFactory(comp);
this.compRef = vcRef.createComponent(compFactory, 0, ngModuleRef.injector);
this.loaded = true;
});
}
ngOnDestroy() {
this.compRef.destroy();
}
}
And then use it in your component:
@Component({
selector: 'my-app',
template: `
<h2 class="plunker-title">How to lazy load Angular 2 components in a TabView (PrimeNG)?</h2>
<p-tabView (onChange)="handleChange($event)">
<p-tabPanel header="Home" leftIcon="fa-bar-chart-o">
<home-app></home-app>
</p-tabPanel>
<p-tabPanel header="Hierarquia" leftIcon="fa-sitemap">
<dynamic-container modulePath="./src/modules/tree/tree.module" moduleName="TreeModule"></dynamic-container>
</p-tabPanel>
<p-tabPanel header="Configurações" leftIcon="fa-cog">
<dynamic-container modulePath="./src/modules/config/config.module" moduleName="ConfigModule"></dynamic-container>
</p-tabPanel>
</p-tabView>
`
})
export class AppComponent {
@ViewChildren(DynamicContainerComponent) dynamicContainers: QueryList<DynamicContainerComponent>;
handleChange(e) {
let dynamicContainer = this.dynamicContainers.toArray()[e.index - 1];
if (!dynamicContainer || dynamicContainer.inited) return;
// prevent fast clicking and double loading
dynamicContainer.inited = true;
}
}
See also
After much research, I could solve the problem using router. Now the application is really fast.
app.component.ts:
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
templateUrl: 'app/app.component.html',
selector: 'my-app'
})
export class AppComponent {
constructor(
private router: Router) {
}
handleChange(e) {
let index = e.index;
let link;
switch (index) {
case 0:
link = ['/home'];
break;
case 1:
link = ['/hierarquia'];
break;
case 2:
link = ['/config'];
break;
}
this.router.navigate(link);
}
}
app.component.html:
<div>
<p-tabView (onChange)="handleChange($event)">
<p-tabPanel header="Home" leftIcon="fa-bar-chart-o"></p-tabPanel>
<p-tabPanel header="Hierarquia" leftIcon="fa-sitemap"></p-tabPanel>
<p-tabPanel header="Configurações" leftIcon="fa-cog"></p-tabPanel>
</p-tabView>
</div>
<router-outlet></router-outlet>
app.route.ts:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AppHome } from './app.home';
import { AppTree } from './app.tree';
import { AppConfig } from './app.config';
const routes: Routes = [
{
path: 'home',
component: AppHome
},
{
path: 'hierarquia',
component: AppTree
},
{
path: 'config',
component: AppConfig
},
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routedComponents = [AppHome, AppTree, AppConfig];
app.module.ts:
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import 'rxjs/add/operator/toPromise';
import { AppConfig } from './app.config';
import { AppHeader } from './app.header';
import { AppHome } from './app.home';
import { AppTree } from './app.tree';
import { AppComponent } from './app.component';
import { AppRoutingModule, routedComponents } from './app.route';
import { InputTextModule, DataTableModule, ButtonModule, DialogModule, TabViewModule, ChartModule, TreeModule, GrowlModule, InputSwitchModule, BlockUIModule, InputMaskModule, DropdownModule } from 'primeng/primeng';
@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpModule, AppRoutingModule, InputTextModule, DataTableModule, ButtonModule, DialogModule, TabViewModule, ChartModule, TreeModule, GrowlModule, InputSwitchModule, BlockUIModule, InputMaskModule, DropdownModule],
declarations: [AppHeader, AppComponent, AppHome, AppTree, AppConfig, routedComponents],
bootstrap: [AppHeader, AppComponent]
})
export class AppModule { }
Thanks God! =]
I've tried lazy attribute which doesn't work. Using router and ModuleLoader are great but a bit complex. If you want to keep your application not too much complex, the easiest solution is to use NgIf for rendering tabs.
<p-tabView (onChange)="handleChange($event)">
<p-tabPanel header="Home" leftIcon="fa-bar-chart-o">
<home-app *ngIf="activeTab === 0"></home-app>
</p-tabPanel>
<p-tabPanel header="Hierarquia" leftIcon="fa-sitemap">
<tree-app *ngIf="activeTab === 1"></tree-app>
</p-tabPanel>
<p-tabPanel header="Configurações" leftIcon="fa-cog">
<config-app *ngIf="activeTab === 2"></config-app>
</p-tabPanel>
</p-tabView>
And define a flag to render the selected tab.
handleChange(e) {
this.activeTab = e.index;
}
Since this question is quite old, I don't know if this will be of any use, but I stumbled upon this problem too, and the answer was right in the PrimeNG documentation:
Lazy loading helps initial load performance by only initializing the active tab, inactive tabs are not initialized until they get selected. A lazy loaded tabpanel contents are cached by default so that upon reselection, they are not created again. You may use cache property on TabPanel to configure this behavior. A TabPanel is specified as lazy when there is a ngTemplate with pTemplate="content" in it.
from https://www.primefaces.org/primeng/#/tabview
Even though this is only in the V7 documentation, this behavior works just as well in the V5.2 that I'm working with.
I could verify it by checking the "network" tab in the Chrome DevTools, each tabPanel loads separately as expected. However, the cache property doesn't seem to exist, so it will always be cached.
So for the author, he could do:
<p-tabView>
<p-tabPanel header="Home" leftIcon="fa-bar-chart-o">
<ng-template pTemplate="content">
<home-app></home-app>
</ng-template>
</p-tabPanel>
<p-tabPanel header="Hierarquia" leftIcon="fa-sitemap">
<ng-template pTemplate="content">
<tree-app></tree-app>
</ng-template>
</p-tabPanel>
<p-tabPanel header="Configurações" leftIcon="fa-cog">
<ng-template pTemplate="content">
<config-app></config-app>
</ng-template>
</p-tabPanel>
</p-tabView>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With