2017-08-25 5 views
3

J'ai développé un composant d'onglet personnalisé en utilisant Angular 2 qui fonctionne correctement. Ceci est l'utilisation de celui-ci.Angular 2 chargeant dynamiquement les onglets lorsque l'en-tête clique

<us-tab-verticle> 
    <vtab-content [tabTitle]="'Basic Information'"><basic-info> </basic-info></vtab-content> 
    <vtab-content [tabTitle]="'VAT Settings'"><vat-settings> </vat-settings></vtab-content> 
    <vtab-content [tabTitle]= "'Economy Settings'"><economy-settings> </economy-settings></vtab-content> 
    <vtab-content [tabTitle]="'Access Profiles'"><access-profiles> </access-profiles></vtab-content> 
</us-tab-verticle> 

Le problème est tous les composants de l'onglet se chargent lorsque la charge de la vue.

L'implémentation de mon onglet est la suivante.

nous-tab-verticle.component.html

<div class="v-tabs"> 
    <div class="v-tabs-col v-tabs-left"> 
    <ul class="nav nav-v-tabs flex-column" role="tablist"> 
     <li class="nav-v-item" *ngFor="let tab of tabs" (click)="selectTab(tab)"> 
     <a [class.active]="tab.active" class="nav-v-link" data-toggle="tab" href="#" role="tab">{{tab.title}}</a> 
     </li> 
    </ul> 
    </div> 

    <div class="v-tabs-col v-tabs-fill"> 
    <div class="v-tabs-content"> 
     <div> 
     <ng-content></ng-content> 
     </div> 
    </div> 
    </div> 

nous-tab-verticle.component.ts

import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core'; 
import { VtabContentComponent } from './vtab-content/vtab-content.component'; 

@Component({ 
    selector: 'us-tab-verticle', 
    templateUrl: './us-tab-verticle.component.html', 
    styleUrls: ['./us-tab-verticle.component.scss'] 
}) 
export class UsTabVerticleComponent implements AfterContentInit { 

    @ContentChildren(VtabContentComponent) tabs: QueryList<VtabContentComponent>; 

    // contentChildren are set 
    ngAfterContentInit() { 
    // get all active tabs 
    const activeTabs = this.tabs.filter((tab) => tab.active); 

    // if there is no active tab set, activate the first 
    if (activeTabs.length === 0) { 
     this.selectTab(this.tabs.first); 
    } 
    } 

    selectTab(tab: VtabContentComponent) { 
    // deactivate all tabs 
    this.tabs.toArray().forEach(tab => tab.active = false); 

    // activate the tab the user has clicked on. 
    tab.active = true; 
    } 

} 

VTAB-content.component.html

<div class="tab-content v-tab-content align-content-stretch"> 
    <div [hidden]="!active" class="tab-pane active" role="tabpanel"> 
    <ng-content></ng-content> 
    </div> 
</div> 

VTAB-content.component.ts

import { Component, Input } from '@angular/core'; 
import { NgComponentOutlet } from '@angular/common'; 

@Component({ 
    selector: 'vtab-content', 
    templateUrl: './vtab-content.component.html', 
    styleUrls: ['./vtab-content.component.scss'] 
}) 

export class VtabContentComponent { 
    @Input('tabTitle') title: string; 
    @Input() active = false; 
} 

je dois charger chaque composant lorsque je clique sur l'en-tête des onglets chacun. Je sème que NgComponentOutlet peut utiliser dans ce genre de situations. Mais n'a pas pu avoir une idée de la façon de mettre en œuvre.

+0

Utilisez les 'composants dynamiques 'ou' chargement paresseux 'de composants –

+0

Pour ce problème, nous avons plusieurs façons de le résoudre, je vous suggère de chercher les [onglets material2] (https://github.com/angular/material2/tree/master/src/lib/tabs) qui a une solution implémentée. –

+0

vous pouvez utiliser * ngIf = "active" au lieu de [hidden] de cette façon, il n'insèrera le composant que s'il est vrai – fastAsTortoise

Répondre

1

Je tente de reproduire votre structure avec l'utilisation de la directive ngComponentOutlet. Voici l'onglet conteneur:

@Component({ 
    selector: 'tab', 
    template: '' 
}) 
export class TabComponent { 
    @Input() title: string; 
    @Input() contentRef: any; 
    active = false; 
} 

Ceci est un élément très simple qui connaît son propre nom de l'onglet, un état actif et la référence des composants du corps qui devrait être chargé lorsque quelqu'un sélectionne l'onglet.

Ensuite, nous créons plusieurs composants du corps qui seront chargés dynamiquement:

@Component({ 
    selector: 'tab-content', 
    template: `<p>Hey</p>` 
}) 
export class TabContentComponent {} 

@Component({ 
    selector: 'tab-content-alternative', 
    template: `<p>Hey, this is an alternative content</p>` 
}) 
export class TabContentAlternativeComponent {} 

Voici le composant onglets contenant des onglets de rendu et un espace réservé vide pour les composants du corps dynamique:

@Component({ 
    selector: 'tab-container', 
    template: ` 
    <div class="tab-header"> 
     <div class="tab" *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active">{{tab.title}}</div> 
    </div> 

    <div class="tab-content"> 
     <ng-container *ngComponentOutlet="content | async"></ng-container> 
    </div> 
    `, 
}) 
export class TabContainerComponent implements AfterContentInit { 
    @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; 

    private contentSbj = new BehaviorSubject<BasicContent>(null); 
    content = this.contentSbj.asObservable(); 

    ngAfterContentInit() { 
    const activeTabs = this.tabs.filter((tab) => tab.active); 
    if (activeTabs.length === 0) { 
     this.selectTab(this.tabs.first); 
    } 
    } 

    selectTab(tab: TabComponent) { 
    this.tabs.toArray().forEach(tab => tab.active = false); 
    tab.active = true; 
    this.contentSbj.next(tab.contentRef); 
    } 
} 

Et Voici comment il peut être utilisé dans certains composants parents:

import {TabContentComponent} from './tab/tab-content.component' 
import {TabContentAlternativeComponent} from './tab/tab-content-alternative.component' 
... 

@Component({ 
    selector: 'my-app', 
    template: ` 
    <tab-container> 
     <tab title="Tab 1" [contentRef]="normalContent"></tab> 
     <tab title="Tab 2" [contentRef]="alternativeContent"></tab> 
    </tab-container> 
    `, 
}) 
export class App { 
    normalContent = TabContentComponent; 
    alternativeContent = TabContentAlternativeComponent; 
} 

Ici fonctionne Plunkr

+0

Selon votre solution 'title-content-mapping' est une dépendance de TabContainerComponent. Donc, cela ne peut pas utiliser comme un élément commun. Ma mise en œuvre actuelle peut utiliser n'importe où du projet et peut créer un nombre quelconque d'onglets. –

+0

L'idée était d'avoir une certaine correspondance entre les noms d'onglet et leur contenu. Je peux m'en débarrasser et passer directement un composant de contenu dans un onglet en tant que paramètre d'entrée. Mise à jour le [plunkr] (https://embed.plnkr.co/OUYzd445JXETcmZdP4Q9/) légèrement. – hiper2d

+0

S'il vous plaît consulter ma solution finale dans ce [plunkr] (https://embed.plnkr.co/OvtQQnlF9NtcODjRrDQm/). Maintenant, le composant conteneur de tabulation peut avoir autant d'onglets que nécessaire (voir app.ts) sans aucune dépendance externe et toute la logique est cachée à l'intérieur. Mis à jour la réponse aussi. – hiper2d

2

Il existe de nombreuses solutions, si vous voulez améliorer votre solution, je suggère l'utilisation de *ngIf. Mais notez que vous détruisez et créez un nouveau composant chaque fois que l'état *ngIf change.

Je vous suggère de jeter un oeil à RouterModule avec l'utilisation de children.

Un petit échantillon: (pas sûr que vous pouvez le faire fonctionner tout de suite mais j'utiliser des trucs similaires dans mon application)

// Router.ts 
import { NgModule } from '@angular/core'; 
import { Routes, RouterModule } from '@angular/router'; 

const routes: Routes = [ 
    { 
     path: '', 
     children: [ 
      { path: 'tab1', component: Tab1Component }, 
      { path: 'tab2', component: Tab2Component }, 
     ] 
    }, 
    { path: '**', redirectTo: '' } // modify for default tab? 
]; 


@NgModule({ 
    imports: [RouterModule.forRoot(routes)], 
    exports: [RouterModule] 
}) 
export class AppRoutingModule { } 

export const routedComponents = [Tab1Component, Tab2Component]; 

// AppModule.ts 
imports: [AppRoutingModule] 
declarations: [ 
    routedComponents 
], 

// main.html 
<app-header> 
<us-tab-verticle> 
<div class="tab" [routerLink]="['/tab1']"><a> 
<div class="tab" [routerLink]="['/tab2']"><a> 
<router-outlet></router-outlet> // the content of your tab components will be displayed below 
<app-footer> 

De cette façon, à mon avis, rendre votre application plus facile à lire (routage déclarative) au lieu d'avoir un service externe et un composant pour gérer les états de vos onglets.

+0

* ngIf ne fonctionne pas avec ma mise en œuvre. Je vais essayer avec l'option de changement de routeur. –