【问题标题】:Angular 2 dynamically loading tabs when header clickAngular 2在标题单击时动态加载选项卡
【发布时间】:2017-08-25 09:00:42
【问题描述】:

我使用 Angular 2 开发了一个自定义选项卡组件,它运行良好。 这是它的用法。

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

问题是视图加载时所有选项卡组件都在加载。

我的tab实现如下。

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

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

单击每个选项卡的标题时,我需要加载每个组件。 我播种NgComponentOutlet 可以用于这种情况。但不知道如何实现。

【问题讨论】:

  • 利用dynamic componentslazy loading 的组件
  • 对于这个问题,我们有很多方法可以解决,建议你找有解决方案的material2 tabs
  • 你可以使用 *ngIf="active" 而不是 [hidden] 这样它只会在 true 时插入组件

标签: javascript angular tabs angular2-directives


【解决方案1】:

有很多解决方案,如果您想改进您的解决方案,我建议使用*ngIf。但请注意,每次*ngIf 状态更改时,您都会销毁并创建一个新组件。

我建议您使用children 属性查看RouterModule

一个小示例:(不确定您是否可以立即使其工作,但我在我的应用程序中使用了类似的东西)

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

在我看来,这种方式可以让您的应用更易于阅读(声明式路由),而不是使用外部服务和组件来管理选项卡的状态。

【讨论】:

  • *ngIf 不适用于我的实现。我将尝试使用路由器更改选项。
【解决方案2】:

我尝试使用ngComponentOutlet 指令来重现您的结构。这是标签容器:

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

这是一个非常简单的组件,它知道自己的选项卡名称、活动状态以及当有人选择选项卡时应该加载的主体组件引用。

然后我们创建几个动态加载的body组件:

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

这是带有标签渲染的标签容器组件和动态正文组件的空占位符:

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

这就是它在某些父组件中的使用方式:

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

这里正在工作Plunkr

【讨论】:

  • 根据您的解决方案title-content-mapping 是 TabContainerComponent 的依赖项。所以这不能作为通用组件使用。我当前的实现可以使用项目的任何位置,并且可以创建任意数量的选项卡。
  • 这个想法是在选项卡名称和它们的内容之间进行一些映射。我可以摆脱它并直接将内容组件作为输入参数传递到选项卡中。稍微更新了plunkr
  • 请在plunkr 中查看我的最终解决方案。现在选项卡容器组件可以根据需要拥有任意数量的选项卡(参见 app.ts),无需任何外部依赖,所有逻辑都隐藏在内部。也更新了答案。
猜你喜欢
  • 2016-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-12
  • 1970-01-01
  • 2011-05-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多