Last active
March 8, 2021 10:54
-
-
Save 2J/7e486786a056b24cfc816bff946b61a3 to your computer and use it in GitHub Desktop.
Angular 2 tabs example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<tabs> | |
<tab tabTitle="Tab 1">Tab 1 Content</tab> | |
<tab tabTitle="Tab 2" [active]="true">Tab 2 Content</tab> | |
<tab tabTitle="Tab 3" [disabled]="true">Tab 3 Content</tab> | |
<tab tabTitle="Link Tab 4" href="http://google.com">Link Tab</tab> | |
<tab tabTitle="Link Tab 5" href="http://google.com" [disabled]="true">Disabled Link Tab</tab> | |
</tabs> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This barrel file provides the export for the shared TabsComponent. | |
*/ | |
export { TabsComponent } from './tabs.component'; | |
export { TabComponent } from './tab.component'; | |
export { OnTabSelect } from './on-tab-select.interface'; | |
export { OnTabDeselect } from './on-tab-deselect.interface'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Runs onTabDeselected() function when tab containing component is deselected | |
export abstract class OnTabDeselect { | |
abstract onTabDeselected(): void; | |
} | |
/** | |
* 1. import OnTabDeselect and implement in class: | |
* class ExampleComponent implements OnTabDeselect { onTabDeselected(){ } } | |
* 2. Add to component providers: | |
* providers: [{ provide: OnTabDeselect, useExisting: forwardRef(() => ExampleComponent) }] | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Runs onTabSelected() function when tab containing component is selected | |
export abstract class OnTabSelect { | |
abstract onTabSelected(): void; | |
} | |
/** | |
* 1. import OnTabSelect and implement in class: | |
* class ExampleComponent implements OnTabSelect { onTabSelected(){ } } | |
* 2. Add to component providers: | |
* providers: [{ provide: OnTabSelect, useExisting: forwardRef(() => ExampleComponent) }] | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, Input, ContentChildren, QueryList, Output, EventEmitter } from '@angular/core'; | |
import { OnTabSelect } from './on-tab-select.interface'; | |
import { OnTabDeselect } from './on-tab-deselect.interface'; | |
@Component({ | |
selector: 'tab', | |
styles: [` | |
.pane{ | |
padding: 1em; | |
} | |
`], | |
template: ` | |
<div [hidden]="!active" class="pane"> | |
<ng-content></ng-content> | |
</div> | |
` | |
}) | |
export class TabComponent { | |
@Input() tabId: any; | |
@Input() tabTitle: string; | |
@Output() | |
selected: EventEmitter<null> = new EventEmitter<null>(); | |
@Output() | |
deselected: EventEmitter<null> = new EventEmitter<null>(); | |
private _active: boolean = false; | |
@Input() | |
get active(): boolean { | |
return this._active; | |
} | |
set active(active: boolean) { | |
let changed: boolean = this.active !== active; | |
this._active = active; | |
//Run onTabSelected for all tab children if active changed from false to true | |
if (changed && this.active === true) { | |
for (let onTabSelectComponent of this.onTabSelectComponents.toArray()) { | |
onTabSelectComponent.onTabSelected(); | |
} | |
this.selected.emit(); | |
} | |
if (changed && this.active === false) { | |
for (let onTabDeselectComponent of this.onTabDeselectComponents.toArray()) { | |
onTabDeselectComponent.onTabDeselected(); | |
} | |
this.deselected.emit(); | |
} | |
} | |
@Input() disabled: boolean = false; | |
@Input() href: string = ''; //TODO: Add option to make into link or routerlink | |
@ContentChildren(OnTabSelect) | |
onTabSelectComponents: QueryList<OnTabSelect>; | |
@ContentChildren(OnTabDeselect) | |
onTabDeselectComponents: QueryList<OnTabDeselect>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, ContentChildren, QueryList, AfterContentInit, EventEmitter, Output } from '@angular/core'; | |
import { TabComponent } from './tab.component'; | |
import * as _ from 'lodash'; | |
@Component({ | |
selector: 'tabs', | |
template: ` | |
<ul class="nav nav-tabs"> | |
<li *ngFor="let tab of tabs" [class.active]="tab.active" [class.disabled]="tab.disabled"> | |
<a role="tab" [href]="tab.href" (click)="selectTab(tab, $event)"> | |
{{tab.tabTitle}} | |
</a> | |
</li> | |
</ul> | |
<ng-content></ng-content> | |
` | |
}) | |
export class TabsComponent implements AfterContentInit { | |
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>; | |
@Output() tabChangeEmitter = new EventEmitter<number>(); | |
// contentChildren are set | |
ngAfterContentInit() { | |
// get all active tabs | |
let 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); | |
} | |
} | |
findTabIndexById(id: any): number { | |
return _.findIndex(this.tabs.toArray(), { tabId: id }); | |
/*//non-lodash implementation | |
let tabIndex = 0; | |
for (let tab of this.tabs.toArray()) { | |
if (tab.tabId === id) { | |
return tabIndex; | |
} | |
else { | |
++tabIndex; | |
} | |
} | |
return null;*/ | |
} | |
findTabIndexByTitle(title: string): number { | |
return _.findIndex(this.tabs.toArray(), { tabTitle: title }); | |
/*//non-lodash implementation | |
let tabIndex = 0; | |
for (let tab of this.tabs.toArray()) { | |
if (tab.tabTitle === title) { | |
return tabIndex | |
} | |
else { | |
++tabIndex; | |
} | |
} | |
return null;*/ | |
} | |
selectTab(tab: TabComponent, event: Event = null, force: boolean = false): void { | |
let tabs = this.tabs.toArray(); | |
if ((force || !tab.disabled) && !tab.href) { | |
let hasChanged = tab.active === false; | |
this.tabs.toArray().forEach(t => t.active = t === tab); | |
if (hasChanged) { | |
//Send event that active tab has changed | |
this.tabChangeEmitter.emit(this.currentIndex()); | |
} | |
} | |
if (!!event && (!tab.href || (tab.href && (force || !tab.disabled)))) { | |
event.preventDefault(); | |
} | |
} | |
selectTabByIndex(index: number): boolean { | |
let tabs = this.tabs.toArray(); | |
if (index in tabs) { | |
//TODO: currently forcing tab change even if disabled because of current implementation | |
this.selectTab(tabs[index], null, true); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
selectTabById(id: any): boolean { | |
let tabs = this.tabs.toArray(); | |
let index = this.findTabIndexById(id); | |
if (index in tabs) { | |
//TODO: currently forcing tab change even if disabled because of current implementation | |
this.selectTab(tabs[index], null, true); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
selectTabOffset(offset: number): boolean { | |
let currentIndex = this.currentIndex(); | |
if (currentIndex === null) return false; | |
if ((currentIndex + offset) in this.tabs.toArray()) { | |
this.selectTabByIndex(currentIndex + offset); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
selectTabNext(offset: number = 1): boolean { | |
return this.selectTabOffset(offset); | |
} | |
selectTabPrev(offset: number = 1): boolean { | |
return this.selectTabOffset(-offset); | |
} | |
currentIndex(): number { | |
for (let index in this.tabs.toArray()) { | |
if (this.tabs.toArray()[index].active) { | |
return Number(index); | |
} | |
} | |
return null; //no active tab found | |
} | |
} |
How where must I import OnTabSelect and OnTabDeselect?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TypeError: Cannot read property 'toArray' of undefined