最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular 2 dynamically loading tabs when header click - Stack Overflow

programmeradmin2浏览0评论

I have developed a custom tab ponent using Angular 2 which is working fine. This is the usage of it.

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

The problem is all the tab ponents are loading when the view load.

My tab implementation is as follows.

us-tab-verticleponent.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>

us-tab-verticleponent.ts

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

@Component({
  selector: 'us-tab-verticle',
  templateUrl: './us-tab-verticleponent.html',
  styleUrls: ['./us-tab-verticleponent.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-contentponent.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-contentponent.ts

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

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

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

I need to load each ponent when I click the header of the each tabs. I sow that NgComponentOutlet can use to this kind of situations. But could not get any idea how to implement.

I have developed a custom tab ponent using Angular 2 which is working fine. This is the usage of it.

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

The problem is all the tab ponents are loading when the view load.

My tab implementation is as follows.

us-tab-verticle.ponent.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>

us-tab-verticle.ponent.ts

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

@Component({
  selector: 'us-tab-verticle',
  templateUrl: './us-tab-verticle.ponent.html',
  styleUrls: ['./us-tab-verticle.ponent.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.ponent.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.ponent.ts

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

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

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

I need to load each ponent when I click the header of the each tabs. I sow that NgComponentOutlet can use to this kind of situations. But could not get any idea how to implement.

Share Improve this question edited Aug 28, 2017 at 8:09 K Scandrett 16.5k4 gold badges42 silver badges68 bronze badges asked Aug 25, 2017 at 9:00 Janith WidarshanaJanith Widarshana 3,48310 gold badges55 silver badges77 bronze badges 3
  • make use of dynamic ponents or lazy loading of poennts – Rahul Singh Commented Aug 30, 2017 at 7:27
  • For this issue, we have many ways to solve it, I suggest you look for the material2 tabs that has a solution implemented. – Leonardo Oliveira Commented Aug 31, 2017 at 14:02
  • you can use *ngIf="active" instead of [hidden] that way it will only insert the ponent when true – fastAsTortoise Commented Aug 31, 2017 at 15:01
Add a ment  | 

2 Answers 2

Reset to default 3

There are many solutions, if you want to improve your solution i suggest the usage of *ngIf. But notice that you destroy and create a new Component every time the *ngIf state changes.

I suggest you to take a look at RouterModule with the use of children attribue.

A little sample : (not sure you can make it work right away but I use similar stuff in my app)

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

const routes: Routes = [
    {
        path: '',
        children: [
            { path: 'tab1', ponent: Tab1Component },
            { path: 'tab2', ponent: 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 ponents will be displayed below
<app-footer>

This way in my opinion make your app easier to read (declarative routing) instead of having an external service and ponent for managing the states of your tabs.

I try to reproduce your structure with the usage of the ngComponentOutlet directive. Here is the tab-container:

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

This is a very simple ponent which knows its own tab name, an active state and the body ponent reference which should be loaded when somebody selects the tab.

Then we create several body ponents which will be loaded dynamically:

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

Here is the tabs-container ponent with tabs rendering and an empty placeholder for dynamic body ponents:

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

And this is how it can be used in some parent ponent:

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

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

Here is working Plunkr

发布评论

评论列表(0)

  1. 暂无评论