How to lazy load Angular 2 components in a TabView (PrimeNG)?

前端 未结 5 827
野的像风
野的像风 2020-12-30 16:37

It is my app.component.ts:

import { Component } from \'@angular/core\';

@Component({
    templateUrl: \'app/app.component.html\',
    selector: \'my-app\'
}         


        
相关标签:
5条回答
  • 2020-12-30 16:49

    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

    • How to manually lazy load a module?
    0 讨论(0)
  • 2020-12-30 16:49

    Primeng tabview has an "lazy" attribute which is default to "false". You can set it as follows

    <p-tabView [lazy]="true">
    
    0 讨论(0)
  • 2020-12-30 16:52

    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! =]

    0 讨论(0)
  • 2020-12-30 17:09

    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>
    
    0 讨论(0)
  • 2020-12-30 17:14

    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;
    }
    
    0 讨论(0)
提交回复
热议问题