### File

`src/tabs/tab-headers.component.ts`

### Description

The `TabHeaders` component contains the `Tab` items and controls scroll functionality if content has overflow.

### Implements

`AfterContentInit` `OnChanges`

### Metadata

selector | `ibm-tab-headers`
template |
    <nav
    	class="bx--tabs"
    	[ngClass]="{
    		'bx--skeleton': skeleton
    	}"
    	role="navigation"
    	[attr.aria-label]="ariaLabel"
    	[attr.aria-labelledby]="ariaLabelledby">
    	<div
    		class="bx--tabs-trigger"
    		tabindex="0"
    		(click)="showTabList()"
    		(keydown)="onDropdownKeydown($event)">
    		<a
    			href="javascript:void(0)"
    			class="bx--tabs-trigger-text"
    			tabindex="-1">
    			<ng-container *ngIf="!getSelectedTab().headingIsTemplate">
    				{{ getSelectedTab().heading }}
    			</ng-container>
    			<ng-template
    				*ngIf="getSelectedTab().headingIsTemplate"
    				[ngTemplateOutlet]="getSelectedTab().heading"
    				[ngTemplateOutletContext]="{$implicit: getSelectedTab().context}">
    			</ng-template>
    		</a>
    		<svg width="10" height="5" viewBox="0 0 10 5">
    			<path d="M0 0l5 4.998L10 0z" fill-rule="evenodd"></path>
    		</svg>
    	</div>
    	<ul
    		#tabList
    		[ngClass]="{
    			'bx--tabs__nav--hidden': !tabListVisible
    		}"
    		class="bx--tabs__nav"
    		role="tablist">
    		<li role="presentation">
    			<ng-container *ngIf="contentBefore" [ngTemplateOutlet]="contentBefore"></ng-container>
    		</li>
    		<li
    			*ngFor="let tab of tabs; let i = index;"
    			[ngClass]="{
    				'bx--tabs__nav-item--selected': tab.active,
    				'bx--tabs__nav-item--disabled': tab.disabled
    			}"
    			class="bx--tabs__nav-item"
    			role="presentation"
    			(click)="selectTab(tabItem, tab, i)"
    			(keydown)="tabDropdownKeydown($event)">
    			<a
    				#tabItem
    				[attr.aria-selected]="tab.active"
    				[attr.tabindex]="(tab.active?0:-1)"
    				[attr.aria-controls]="tab.id"
    				(focus)="onTabFocus(tabItem, i)"
    				draggable="false"
    				id="{{tab.id}}-header"
    				class="bx--tabs__nav-link"
    				href="javascript:void(0)"
    				role="tab">
    				<ng-container *ngIf="!tab.headingIsTemplate">
    					{{ tab.heading }}
    				</ng-container>
    				<ng-template
    					*ngIf="tab.headingIsTemplate"
    					[ngTemplateOutlet]="tab.heading"
    					[ngTemplateOutletContext]="{$implicit: tab.context}">
    				</ng-template>
    			</a>
    		</li>
    		<li role="presentation">
    			<ng-container *ngIf="contentAfter" [ngTemplateOutlet]="contentAfter"></ng-container>
    		</li>
    	</ul>
    </nav>

### Index

###### Properties
allTabHeaders Public currentSelectedTab Public firstVisibleTab headerContainer Public tabListVisible tabQuery Public tabs
###### Methods
Public getSelectedTab Public hideTabList ngAfterContentInit ngOnChanges Public onDropdownKeydown Public onTabFocus Public selectTab Protected setFirstTab Public showTabList Public tabDropdownKeydown
###### Inputs
ariaLabel ariaLabelledby cacheActive contentAfter contentBefore followFocus skeleton tabs
###### HostListeners
focusout keydown

### Constructor

`constructor(elementRef: ElementRef)`
Defined in src/tabs/tab-headers.component.ts:161
Parameters :
Name | Type | Optional
elementRef | `ElementRef` | No

### Inputs

`ariaLabel` | Sets the aria label on the nav element. Type : `string`
Defined in src/tabs/tab-headers.component.ts:128

`ariaLabelledby` | Sets the aria labelledby on the nav element. Type : `string`
Defined in src/tabs/tab-headers.component.ts:132

`cacheActive` | Set to 'true' to have `Tab` items cached and not reloaded on tab switching. Duplicate from `n-tabs` to support standalone headers Default value : `false`
Defined in src/tabs/tab-headers.component.ts:116

`contentAfter` | Type : `TemplateRef<any>`
Defined in src/tabs/tab-headers.component.ts:135

`contentBefore` | Type : `TemplateRef<any>`
Defined in src/tabs/tab-headers.component.ts:134

`followFocus` | Set to 'true' to have tabs automatically activated and have their content displayed when they receive focus. Type : `boolean`
Defined in src/tabs/tab-headers.component.ts:120

`skeleton` | Set to `true` to put tabs in a loading state. Default value : `false`
Defined in src/tabs/tab-headers.component.ts:124

`tabs` | List of `Tab` components. Type : `QueryList<Tab>`
Defined in src/tabs/tab-headers.component.ts:111

### HostListeners

focusout
Arguments : `'$event'`
`focusout(event)`
Defined in src/tabs/tab-headers.component.ts:235

keydown
Arguments : `'$event'`
`keydown(event)`
Defined in src/tabs/tab-headers.component.ts:170
Controls the keydown events used for tabbing through the headings.

### Methods

Public getSelectedTab
`getSelectedTab()`
Defined in src/tabs/tab-headers.component.ts:271
Returns : `any`

Public hideTabList
`hideTabList()`
Defined in src/tabs/tab-headers.component.ts:288
Returns : `void`

ngAfterContentInit
`ngAfterContentInit()`
Defined in src/tabs/tab-headers.component.ts:241
Returns : `void`

ngOnChanges
`ngOnChanges(changes: SimpleChanges)`
Defined in src/tabs/tab-headers.component.ts:255
Parameters :
Name | Type | Optional
changes | `SimpleChanges` | No
Returns : `void`

Public onDropdownKeydown
`onDropdownKeydown(event: KeyboardEvent)`
Defined in src/tabs/tab-headers.component.ts:292
Parameters :
Name | Type | Optional
event | `KeyboardEvent` | No
Returns : `void`

Public onTabFocus
`onTabFocus(ref: HTMLElement, index: number)`
Defined in src/tabs/tab-headers.component.ts:264
Controls manually focusing tabs. Parameters :
Name | Type | Optional
ref | `HTMLElement` | No
index | `number` | No
Returns : `void`

Public selectTab
`selectTab(ref: HTMLElement, tab: Tab, tabIndex: number)`
Defined in src/tabs/tab-headers.component.ts:345
Selects `Tab` 'tab' and moves it into view on the view DOM if it is not already. Parameters :
Name | Type | Optional
ref | `HTMLElement` | No
tab | `Tab` | No
tabIndex | `number` | No
Returns : `void`

Protected setFirstTab
`setFirstTab()`
Defined in src/tabs/tab-headers.component.ts:361
Determines which `Tab` is initially selected. Returns : `void`

Public showTabList
`showTabList()`
Defined in src/tabs/tab-headers.component.ts:279
Returns : `void`

Public tabDropdownKeydown
`tabDropdownKeydown(event: KeyboardEvent)`
Defined in src/tabs/tab-headers.component.ts:305
Parameters :
Name | Type | Optional
event | `KeyboardEvent` | No
Returns : `void`

### Properties

allTabHeaders
`allTabHeaders: `QueryList<ElementRef>``
Type : `QueryList<ElementRef>`
Decorators : `@ViewChildren('tabItem')`
Defined in src/tabs/tab-headers.component.ts:156
The DOM element containing the `Tab` headings displayed.

Public currentSelectedTab
`currentSelectedTab: `number``
Type : `number`
Defined in src/tabs/tab-headers.component.ts:160
Controls the manual focusing done by tabbing through headings.

Public firstVisibleTab
`firstVisibleTab: `number``
Type : `number`
Default value : `0`
Defined in src/tabs/tab-headers.component.ts:152
The index of the first visible tab.

headerContainer
`headerContainer: ```
Decorators : `@ViewChild('tabList')`
Defined in src/tabs/tab-headers.component.ts:140
Gets the Unordered List element that holds the `Tab` headings from the view DOM.

Public tabListVisible
`tabListVisible: ```
Default value : `false`
Defined in src/tabs/tab-headers.component.ts:161

tabQuery
`tabQuery: `QueryList<Tab>``
Type : `QueryList<Tab>`
Decorators : `@ContentChildren(Tab)`
Defined in src/tabs/tab-headers.component.ts:144
ContentChild of all the n-tabs

Public tabs
`tabs: `QueryList<Tab>``
Type : `QueryList<Tab>`
Defined in src/tabs/tab-headers.component.ts:148
set to tabQuery if tabInput is empty

    import {
    	Component,
    	QueryList,
    	Input,
    	HostListener,
    	ViewChild,
    	ContentChildren,
    	AfterContentInit,
    	ViewChildren,
    	ElementRef,
    	TemplateRef,
    	OnChanges,
    	SimpleChanges
    } from "@angular/core";

    import { Tab } from "./tab.component";

    /**
     * The `TabHeaders` component contains the `Tab` items and controls scroll functionality
     * if content has overflow.
     */
    @Component({
    	selector: "ibm-tab-headers",
    	template: `
    		<nav
    			class="bx--tabs"
    			[ngClass]="{
    				'bx--skeleton': skeleton
    			}"
    			role="navigation"
    			[attr.aria-label]="ariaLabel"
    			[attr.aria-labelledby]="ariaLabelledby">
    			<div
    				class="bx--tabs-trigger"
    				tabindex="0"
    				(click)="showTabList()"
    				(keydown)="onDropdownKeydown($event)">
    				<a
    					href="javascript:void(0)"
    					class="bx--tabs-trigger-text"
    					tabindex="-1">
    					<ng-container *ngIf="!getSelectedTab().headingIsTemplate">
    						{{ getSelectedTab().heading }}
    					</ng-container>
    					<ng-template
    						*ngIf="getSelectedTab().headingIsTemplate"
    						[ngTemplateOutlet]="getSelectedTab().heading"
    						[ngTemplateOutletContext]="{$implicit: getSelectedTab().context}">
    					</ng-template>
    				</a>
    				<svg width="10" height="5" viewBox="0 0 10 5">
    					<path d="M0 0l5 4.998L10 0z" fill-rule="evenodd"></path>
    				</svg>
    			</div>
    			<ul
    				#tabList
    				[ngClass]="{
    					'bx--tabs__nav--hidden': !tabListVisible
    				}"
    				class="bx--tabs__nav"
    				role="tablist">
    				<li role="presentation">
    					<ng-container *ngIf="contentBefore" [ngTemplateOutlet]="contentBefore"></ng-container>
    				</li>
    				<li
    					*ngFor="let tab of tabs; let i = index;"
    					[ngClass]="{
    						'bx--tabs__nav-item--selected': tab.active,
    						'bx--tabs__nav-item--disabled': tab.disabled
    					}"
    					class="bx--tabs__nav-item"
    					role="presentation"
    					(click)="selectTab(tabItem, tab, i)"
    					(keydown)="tabDropdownKeydown($event)">
    					<a
    						#tabItem
    						[attr.aria-selected]="tab.active"
    						[attr.tabindex]="(tab.active?0:-1)"
    						[attr.aria-controls]="tab.id"
    						(focus)="onTabFocus(tabItem, i)"
    						draggable="false"
    						id="{{tab.id}}-header"
    						class="bx--tabs__nav-link"
    						href="javascript:void(0)"
    						role="tab">
    						<ng-container *ngIf="!tab.headingIsTemplate">
    							{{ tab.heading }}
    						</ng-container>
    						<ng-template
    							*ngIf="tab.headingIsTemplate"
    							[ngTemplateOutlet]="tab.heading"
    							[ngTemplateOutletContext]="{$implicit: tab.context}">
    						</ng-template>
    					</a>
    				</li>
    				<li role="presentation">
    					<ng-container *ngIf="contentAfter" [ngTemplateOutlet]="contentAfter"></ng-container>
    				</li>
    			</ul>
    		</nav>
    	`
    })

    export class TabHeaders implements AfterContentInit, OnChanges {
    	/**
    	 * List of `Tab` components.
    	 */
    	// disable the next line because we need to rename the input
    	// tslint:disable-next-line
    	@Input("tabs") tabInput: QueryList<Tab>;
    	/**
    	 * Set to 'true' to have `Tab` items cached and not reloaded on tab switching.
    	 * Duplicate from `n-tabs` to support standalone headers
    	 */
    	@Input() cacheActive = false;
    	/**
    	 * Set to 'true' to have tabs automatically activated and have their content displayed when they receive focus.
    	 */
    	@Input() followFocus: boolean;
    	/**
    	 * Set to `true` to put tabs in a loading state.
    	 */
    	@Input() skeleton = false;
    	/**
    	 * Sets the aria label on the nav element.
    	 */
    	@Input() ariaLabel: string;
    	/**
    	 * Sets the aria labelledby on the nav element.
    	 */
    	@Input() ariaLabelledby: string;

    	@Input() contentBefore: TemplateRef<any>;
    	@Input() contentAfter: TemplateRef<any>;

    	/**
    	 * Gets the Unordered List element that holds the `Tab` headings from the view DOM.
    	 */
    	@ViewChild("tabList") headerContainer;
    	/**
    	 * ContentChild of all the n-tabs
    	 */
    	@ContentChildren(Tab) tabQuery: QueryList<Tab>;
    	/**
    	 * set to tabQuery if tabInput is empty
    	 */
    	public tabs: QueryList<Tab>;
    	/**
    	 * The index of the first visible tab.
    	 */
    	public firstVisibleTab = 0;
    	/**
    	 * The DOM element containing the `Tab` headings displayed.
    	 */
    	@ViewChildren("tabItem") allTabHeaders: QueryList<ElementRef>;
    	/**
    	 * Controls the manual focusing done by tabbing through headings.
    	 */
    	public currentSelectedTab: number;
    	public tabListVisible = false;

    	constructor(protected elementRef: ElementRef) {}

    	// keyboard accessibility
    	/**
    	 * Controls the keydown events used for tabbing through the headings.
    	 */
    	@HostListener("keydown", ["$event"])
    	keyboardInput(event) {
    		let tabsArray = Array.from<any>(this.tabs);

    		// "Right" is an ie11 specific value
    		if (event.key === "Right" || event.key === "ArrowRight") {
    			if (this.currentSelectedTab < this.allTabHeaders.length - 1) {
    				event.preventDefault();
    				if (this.followFocus) {
    					this.selectTab(event.target, tabsArray[this.currentSelectedTab + 1], this.currentSelectedTab);
    				}
    				this.allTabHeaders.toArray()[this.currentSelectedTab + 1].nativeElement.focus();
    			} else {
    				event.preventDefault();
    				if (this.followFocus) {
    					this.selectTab(event.target, tabsArray[0], 0);
    				}
    				this.allTabHeaders.first.nativeElement.focus();
    			}
    		}

    		// "Left" is an ie11 specific value
    		if (event.key === "Left" || event.key === "ArrowLeft") {
    			if (this.currentSelectedTab > 0) {
    				event.preventDefault();
    				if (this.followFocus) {
    					this.selectTab(event.target, tabsArray[this.currentSelectedTab - 1], this.currentSelectedTab);
    				}
    				this.allTabHeaders.toArray()[this.currentSelectedTab - 1].nativeElement.focus();
    			} else {
    				event.preventDefault();
    				if (this.followFocus) {
    					this.selectTab(event.target, tabsArray[this.allTabHeaders.length - 1], this.allTabHeaders.length);
    				}
    				this.allTabHeaders.toArray()[this.allTabHeaders.length - 1].nativeElement.focus();
    			}
    		}

    		if (event.key === "Home") {
    			event.preventDefault();
    			if (this.followFocus) {
    				this.selectTab(event.target, tabsArray[0], 0);
    			}
    			this.allTabHeaders.toArray()[0].nativeElement.focus();
    		}

    		if (event.key === "End") {
    			event.preventDefault();
    			if (this.followFocus) {
    				this.selectTab(event.target, tabsArray[this.allTabHeaders.length - 1], this.allTabHeaders.length);
    			}
    			this.allTabHeaders.toArray()[this.allTabHeaders.length - 1].nativeElement.focus();
    		}

    		// `"Spacebar"` is IE11 specific value
    		if ((event.key === " " || event.key === "Spacebar") && !this.followFocus) {
    			this.selectTab(event.target, tabsArray[this.currentSelectedTab], this.currentSelectedTab);
    		}

    		// dropdown list handler
    		if (event.key === "Escape") {
    			this.hideTabList();
    		}
    	}

    	@HostListener("focusout", ["$event"])
    	focusOut(event) {
    		if (this.tabListVisible && !this.elementRef.nativeElement.contains(event.relatedTarget)) {
    			this.tabListVisible = false;
    		}
    	}

    	ngAfterContentInit() {
    		if (!this.tabInput) {
    			this.tabs = this.tabQuery;
    		} else {
    			this.tabs = this.tabInput;
    		}

    		this.tabs.forEach(tab => tab.cacheActive = this.cacheActive);
    		this.tabs.changes.subscribe(() => {
    			this.setFirstTab();
    		});
    		this.setFirstTab();
    	}

    	ngOnChanges(changes: SimpleChanges) {
    		if (this.tabs && changes.cacheActive) {
    			this.tabs.forEach(tab => tab.cacheActive = this.cacheActive);
    		}
    	}

    	/**
    	 * Controls manually focusing tabs.
    	 */
    	public onTabFocus(ref: HTMLElement, index: number) {
    		if (this.tabListVisible) { return; }
    		this.currentSelectedTab = index;
    		// reset scroll left because we're already handling it
    		this.headerContainer.nativeElement.parentElement.scrollLeft = 0;
    	}

    	public getSelectedTab(): any {
    		const selected = this.tabs.find(tab => tab.active);
    		if (selected) {
    			return selected;
    		}
    		return {headingIsTemplate: false, heading: ""};
    	}

    	public showTabList() {
    		this.tabListVisible = true;
    		const focusTarget = this.allTabHeaders.find(tab => {
    			const tabContainer = tab.nativeElement.parentElement;
    			return !tabContainer.classList.contains("bx--tabs__nav-item--selected");
    		});
    		focusTarget.nativeElement.focus();
    	}

    	public hideTabList() {
    		this.tabListVisible = false;
    	}

    	public onDropdownKeydown(event: KeyboardEvent) {
    		switch (event.key) {
    			case " ":
    			case "Spacebar":
    			case "Enter":
    				event.preventDefault();
    				this.showTabList();
    				break;
    			default:
    				break;
    		}
    	}

    	public tabDropdownKeydown(event: KeyboardEvent) {
    		if (!this.tabListVisible) { return; }
    		const target = (event.target as HTMLElement).closest("a");

    		const headers = this.allTabHeaders.toArray().filter(tab =>
    			!tab.nativeElement.parentElement.classList.contains("bx--tabs__nav-item--disabled") &&
    			!tab.nativeElement.parentElement.classList.contains("bx--tabs__nav-item--selected"));

    		// unless the focus can move, it should remain on the target
    		let next: HTMLElement = target;
    		let previous: HTMLElement = target;

    		for (let i = 0; i < headers.length; i++) {
    			if (headers[i].nativeElement === target) {
    				if (i + 1 < headers.length) {
    					next = headers[i + 1].nativeElement;
    				}
    				if (i - 1 >= 0) {
    					previous = headers[i - 1].nativeElement;
    				}
    			}
    		}

    		switch (event.key) {
    			case "ArrowDown":
    			case "Down": // IE11 specific value
    				next.focus();
    				break;
    			case "ArrowUp":
    			case "Up": // IE11 specific value
    				previous.focus();
    				break;
    			default:
    				break;
    		}
    	}

    	/**
    	 * Selects `Tab` 'tab' and moves it into view on the view DOM if it is not already.
    	 */
    	public selectTab(ref: HTMLElement, tab: Tab, tabIndex: number) {
    		if (tab.disabled) {
    			return;
    		}

    		// hide the tablist on mobile
    		this.tabListVisible = false;
    		this.currentSelectedTab = tabIndex;
    		this.tabs.forEach(_tab => _tab.active = false);
    		tab.active = true;
    		tab.doSelect();
    	}

    	/**
    	 * Determines which `Tab` is initially selected.
    	 */
    	protected setFirstTab() {
    		setTimeout(() => {
    			let firstTab = this.tabs.find(tab => tab.active);
    			if (!firstTab && this.tabs.first) {
    				firstTab = this.tabs.first;
    				firstTab.active = true;
    			}
    			if (firstTab) {
    				firstTab.doSelect();
    			}
    		});
    	}
    }

Legend
Html element
Component
Html element with directive

# result-matching ""

# No results matching ""