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
orlazy 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
2 Answers
Reset to default 3There 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