如何在 TabView (PrimeNG) 中延迟加载 Angular 2 个组件?
How to lazy load Angular 2 components in a TabView (PrimeNG)?
这是我的 app.component.ts:
import { Component } from '@angular/core';
@Component({
templateUrl: 'app/app.component.html',
selector: 'my-app'
})
export class AppComponent {
}
这是我的 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>
我的三个组件(home、tree和config)在加载tabView的时候同时加载了。但是,我希望在选择选项卡时加载组件。怎么做?
P.S.: 如果有帮助,TabView 有一个 onChange 事件。
你可以使用angular2路由中使用的SystemJsNgModuleLoader
首先你可以编写组件来加载模块:
@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();
}
}
然后在你的组件中使用它:
@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;
}
}
另见
经过大量研究,我可以使用 router 解决问题。现在申请速度真快。
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 { }
感谢上帝! =]
Primeng 选项卡视图有一个 "lazy" 属性,默认为 "false"。
您可以设置如下
<p-tabView [lazy]="true">
我试过 lazy 属性,但不起作用。使用 router 和 ModuleLoader 很棒,但有点复杂。如果你想让你的应用程序不要太复杂,最简单的解决方案是使用 NgIf 来渲染选项卡。
<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>
并定义一个标志来呈现选定的选项卡。
handleChange(e) {
this.activeTab = e.index;
}
这个问题很老了,不知道有没有用,不过我也是无意中发现了这个问题,PrimeNG文档里的答案是对的:
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.
来自 https://www.primefaces.org/primeng/#/tabview
尽管这仅出现在 V7 文档中,但此行为 在我使用的 V5.2 中同样有效。
我可以通过检查 Chrome DevTools 中的 "network" 选项卡来验证它,每个选项卡面板都按预期单独加载。
不过缓存属性好像不存在,所以会一直缓存
那么对于作者来说,他可以做到:
<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>
这是我的 app.component.ts:
import { Component } from '@angular/core';
@Component({
templateUrl: 'app/app.component.html',
selector: 'my-app'
})
export class AppComponent {
}
这是我的 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>
我的三个组件(home、tree和config)在加载tabView的时候同时加载了。但是,我希望在选择选项卡时加载组件。怎么做?
P.S.: 如果有帮助,TabView 有一个 onChange 事件。
你可以使用angular2路由中使用的SystemJsNgModuleLoader
首先你可以编写组件来加载模块:
@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();
}
}
然后在你的组件中使用它:
@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;
}
}
另见
经过大量研究,我可以使用 router 解决问题。现在申请速度真快。
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 { }
感谢上帝! =]
Primeng 选项卡视图有一个 "lazy" 属性,默认为 "false"。 您可以设置如下
<p-tabView [lazy]="true">
我试过 lazy 属性,但不起作用。使用 router 和 ModuleLoader 很棒,但有点复杂。如果你想让你的应用程序不要太复杂,最简单的解决方案是使用 NgIf 来渲染选项卡。
<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>
并定义一个标志来呈现选定的选项卡。
handleChange(e) {
this.activeTab = e.index;
}
这个问题很老了,不知道有没有用,不过我也是无意中发现了这个问题,PrimeNG文档里的答案是对的:
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.
来自 https://www.primefaces.org/primeng/#/tabview
尽管这仅出现在 V7 文档中,但此行为 在我使用的 V5.2 中同样有效。
我可以通过检查 Chrome DevTools 中的 "network" 选项卡来验证它,每个选项卡面板都按预期单独加载。 不过缓存属性好像不存在,所以会一直缓存
那么对于作者来说,他可以做到:
<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>