Skip to content

Commit

Permalink
fix(tabs): adding tab content to DOM just if selected tab (#1422) (#4991
Browse files Browse the repository at this point in the history
)

Fixes #1422
  • Loading branch information
Mila authored and valorkin committed Feb 8, 2019
1 parent 5633d2d commit 457c32a
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<tabset [dynamic]="true">
<tab heading="Title" id="tab1">Dynamic insert content</tab>
<tab heading="Title 1">Dynamic insert content 1</tab>
<tab heading="Title 2">Dynamic insert content 2</tab>
</tabset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Component } from '@angular/core';

@Component({
selector: 'demo-tabs-dynamic-insert',
templateUrl: './dynamic-insert.html'
})
export class DemoTabsDynamicInsertComponent {}
4 changes: 3 additions & 1 deletion demo/src/app/components/+tabs/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { DemoTabsDisabledComponent } from './disabled/disabled';
import { DemoTabsCustomComponent } from './custom-template/custom-template';
import { DemoTabsSelectEventComponent } from './select-event/select-event';
import { DemoAccessibilityComponent } from './accessibility/accessibility';
import { DemoTabsDynamicInsertComponent } from "./dynamic-insert/dynamic-insert";

export const DEMO_COMPONENTS = [
DemoTabsBasicComponent,
Expand All @@ -23,5 +24,6 @@ export const DEMO_COMPONENTS = [
DemoTabsConfigComponent,
DemoTabsCustomComponent,
DemoTabsSelectEventComponent,
DemoAccessibilityComponent
DemoAccessibilityComponent,
DemoTabsDynamicInsertComponent
];
11 changes: 11 additions & 0 deletions demo/src/app/components/+tabs/tabs-section.list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { DemoTabsDisabledComponent } from './demos/disabled/disabled';
import { DemoTabsCustomComponent } from './demos/custom-template/custom-template';
import { DemoTabsSelectEventComponent } from './demos/select-event/select-event';
import { DemoAccessibilityComponent } from './demos/accessibility/accessibility';
import { DemoTabsDynamicInsertComponent } from './demos/dynamic-insert/dynamic-insert';

import { ContentSection } from '../../docs/models/content-section.model';
import { DemoTopSectionComponent } from '../../docs/demo-section-components/demo-top-section/index';
Expand Down Expand Up @@ -63,6 +64,16 @@ export const demoComponentContent: ContentSection[] = [
html: require('!!raw-loader?lang=markup!./demos/dynamic/dynamic.html'),
outlet: DemoTabsDynamicComponent
},
{
title: 'Dynamic insert tabs content into DOM',
anchor: 'dynamic-insert',
component: require('!!raw-loader?lang=typescript!./demos/dynamic-insert/dynamic-insert'),
html: require('!!raw-loader?lang=markup!./demos/dynamic-insert/dynamic-insert.html'),
description: `<p>By default, the tabs contents are added into DOM when page is loaded, for all tabs even not
selected. Dynamic insert tabs into DOM will initialize and add the tab directives only when the tab is activated.
Tab contents can be dynamic insert by declaring the tabset component with attribute <code>[dynamic]="true"</code>.</p>`,
outlet: DemoTabsDynamicInsertComponent
},
{
title: 'Pills',
anchor: 'tabs-Pills',
Expand Down
5 changes: 5 additions & 0 deletions demo/src/ng-api-doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2925,6 +2925,11 @@ export const ngdoc: any = {
"name": "vertical",
"type": "boolean",
"description": "<p>if true tabs will be placed vertically </p>\n"
},
{
"name": "dynamic",
"type": "boolean",
"description": "<p>if true, tab content will be insert into DOM only if tab is selected</p>\n"
}
],
"outputs": [],
Expand Down
1 change: 1 addition & 0 deletions src/spec/tabset.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ describe('Component: Tabs', () => {

it('should mark the requested tab as active', () => {
context.tabs[0].active = true;
context.tabset.selectTab(context.tabset.tabs[0]);
fixture.detectChanges();
expectActiveTabs(element, [false, true, false, false]);
});
Expand Down
1 change: 1 addition & 0 deletions src/tabs/tab.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class TabDirective implements OnInit, OnDestroy {
@Input() disabled: boolean;
/** if true tab can be removable, additional button will appear */
@Input() removable: boolean;

/** if set, will be added to the tab's class attribute. Multiple classes are supported. */
@Input()
get customClass(): string {
Expand Down
20 changes: 14 additions & 6 deletions src/tabs/tabset.component.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
<ul class="nav" [ngClass]="classMap" (click)="$event.preventDefault()">
<li *ngFor="let tabz of tabs" [ngClass]="['nav-item', tabz.customClass || '']"
[class.active]="tabz.active" [class.disabled]="tabz.disabled">
<li *ngFor="let tabz of tabs"
[ngClass]="['nav-item', tabz.customClass || '']"
[class.active]="tabz.active"
[class.disabled]="tabz.disabled">
<a href="javascript:void(0);" class="nav-link"
[attr.id]="tabz.id ? tabz.id + '-link' : ''"
[class.active]="tabz.active" [class.disabled]="tabz.disabled"
(click)="tabz.active = true">
[class.active]="tabz.active"
[class.disabled]="tabz.disabled"
(click)="selectTab(tabz)">
<span [ngTransclude]="tabz.headingRef">{{ tabz.heading }}</span>
<span *ngIf="tabz.removable" (click)="$event.preventDefault(); removeTab(tabz);" class="bs-remove-tab"> &#10060;</span>
<span *ngIf="tabz.removable" (click)="$event.preventDefault(); removeTab(tabz);" class="bs-remove-tab">
&#10060;
</span>
</a>
</li>
</ul>

<div class="tab-content">
<ng-content></ng-content>
<ng-container *ngIf="!dynamic">
<ng-content></ng-content>
</ng-container>
</div>
44 changes: 41 additions & 3 deletions src/tabs/tabset.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { Component, HostBinding, Input, OnDestroy, Renderer2 } from '@angular/core';
import {
AfterContentInit,
Component,
ContentChild,
ElementRef,
forwardRef,
HostBinding,
Input,
OnDestroy,
Renderer2
} from '@angular/core';

import { TabDirective } from './tab.directive';
import { TabsetConfig } from './tabset.config';
Expand All @@ -8,12 +18,13 @@ import { TabsetConfig } from './tabset.config';
selector: 'tabset',
templateUrl: './tabset.component.html'
})
export class TabsetComponent implements OnDestroy {
export class TabsetComponent implements OnDestroy, AfterContentInit {
/** if true tabs will be placed vertically */
@Input()
get vertical(): boolean {
return this._vertical;
}

set vertical(value: boolean) {
this._vertical = value;
this.setClassMap();
Expand All @@ -24,6 +35,7 @@ export class TabsetComponent implements OnDestroy {
get justified(): boolean {
return this._justified;
}

set justified(value: boolean) {
this._justified = value;
this.setClassMap();
Expand All @@ -34,12 +46,17 @@ export class TabsetComponent implements OnDestroy {
get type(): string {
return this._type;
}

set type(value: string) {
this._type = value;
this.setClassMap();
}

/** if true, tab content will be inject to DOM only if tab is selected */
@Input() dynamic = false;

@HostBinding('class.tab-container') clazz = true;
@ContentChild(forwardRef(() => TabDirective)) content: TabDirective;

tabs: TabDirective[] = [];
classMap: { [key: string]: boolean } = {};
Expand All @@ -49,14 +66,35 @@ export class TabsetComponent implements OnDestroy {
protected _justified: boolean;
protected _type: string;

constructor(config: TabsetConfig, private renderer: Renderer2) {
constructor(config: TabsetConfig, private renderer: Renderer2, private elRef: ElementRef) {
Object.assign(this, config);
}

ngAfterContentInit() {
if (this.dynamic && typeof this.content !== 'undefined') {
const container = this.elRef.nativeElement.querySelector('.tab-content');
this.renderer.appendChild(container, this.content.elementRef.nativeElement);
}
}

ngOnDestroy(): void {
this.isDestroyed = true;
}

selectTab(selectedTab: TabDirective): void {
selectedTab.active = true;

if (this.dynamic) {
const container = this.elRef.nativeElement.querySelector('.tab-content');

while (container.firstChild) {
container.firstChild.remove();
}

this.renderer.appendChild(container, selectedTab.elementRef.nativeElement);
}
}

addTab(tab: TabDirective): void {
this.tabs.push(tab);
tab.active = this.tabs.length === 1 && typeof tab.active === 'undefined';
Expand Down

0 comments on commit 457c32a

Please sign in to comment.