如何在 TabView (PrimeNG) 中延迟加载 Angular 2 组件?

问题描述 投票:0回答:6

这是我的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 事件。

angular typescript lazy-loading tabview primeng
6个回答
7
投票

您可以使用

SystemJsNgModuleLoader
,它在 angular2 路由中使用

活蹦乱跳

首先你可以编写将加载模块的组件:

@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;
  }
}

另请参阅


5
投票

经过大量研究,我可以使用路由器解决问题。现在申请速度真的很快。

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 { }

感谢上帝! =]


3
投票

我尝试过惰性属性,但不起作用。使用 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;
}

1
投票

由于这个问题很老了,我不知道这是否有任何用处,但我也偶然发现了这个问题,答案在PrimeNG文档中是正确的:

延迟加载仅通过初始化活动选项卡来帮助初始加载性能,非活动选项卡在被选中之前不会被初始化。默认情况下会缓存延迟加载的选项卡面板内容,以便在重新选择时不会再次创建它们。您可以使用 TabPanel 上的缓存属性来配置此行为。当存在带有 pTemplate="content" 的 ngTemplate 时,TabPanel 被指定为惰性。

来自 https://www.primefaces.org/primeng/#/tabview

尽管这仅出现在 V7 文档中,但此行为在我正在使用的 V5.2 中也同样有效。

我可以通过检查 Chrome DevTools 中的“网络”选项卡来验证它,每个 tabPanel 按预期单独加载。 不过,cache属性好像不存在,所以会一直被缓存。

所以对于作者来说,他可以这样做:

<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>

0
投票

Primeng tabview 有一个“lazy”属性,默认为“false”。 您可以按如下方式设置

<p-tabView [lazy]="true">

0
投票

p-tabPanel 作品有 [cache]=false 属性。请看片段:

 <p-tabView class="nav-tabs-primary">
  <p-tabPanel [cache]="false" header="Tab1" *ngIf="condition1">
    <ng-template pTemplate="content">
      <child1></child1>
    </ng-template>
  </p-tabPanel>
  <p-tabPanel [cache]="false" header="Tab2" *ngIf="condition2">
    <ng-template pTemplate="content">
      <child2></child2>
    </ng-template>
  </p-tabPanel>
</p-tabView>

如果存在显示/隐藏选项卡的条件,则基于 activeTabIndex 对子组件使用 *ngFor 的方法不起作用,因为选项卡索引可能会根据选项卡集而变化

© www.soinside.com 2019 - 2024. All rights reserved.