A lightweight and flexible Angular tab strip component for MemberJunction applications. This component provides a simple but powerful way to create tabbed interfaces with support for dynamic tabs, custom tab content, and tab management.
npm install @memberjunction/ng-tabstrip
import { MJTabStripModule } from '@memberjunction/ng-tabstrip';
@NgModule({
imports: [
MJTabStripModule,
// other imports
],
// ...
})
export class YourModule { }
<mj-tabstrip
[SelectedTabIndex]="selectedTab"
(TabSelected)="onTabSelected($event)"
(BeforeTabClosed)="onBeforeTabClosed($event)">
<!-- Tab headers -->
<mj-tab [Name]="'Overview'" [TabCloseable]="false">Overview</mj-tab>
<mj-tab [Name]="'Details'" [TabCloseable]="true">Details</mj-tab>
<mj-tab [Name]="'Settings'" [TabCloseable]="true">Settings</mj-tab>
<!-- Tab bodies -->
<mj-tab-body>
<h2>Overview Content</h2>
<p>This is the overview tab content...</p>
</mj-tab-body>
<mj-tab-body>
<h2>Details Content</h2>
<p>This is the details tab content...</p>
</mj-tab-body>
<mj-tab-body>
<h2>Settings Content</h2>
<p>This is the settings tab content...</p>
</mj-tab-body>
</mj-tabstrip>
import { Component } from '@angular/core';
import { TabEvent, TabCancelableEvent, TabClosedEvent } from '@memberjunction/ng-tabstrip';
@Component({
selector: 'app-dynamic-tabs',
template: `
<div class="tab-container">
<button (click)="addNewTab()">Add Tab</button>
<mj-tabstrip
[SelectedTabIndex]="selectedTabIndex"
(TabSelected)="onTabSelected($event)"
(BeforeTabClosed)="onBeforeTabClosed($event)"
(TabClosed)="onTabClosed($event)"
#tabStrip>
<mj-tab
*ngFor="let tab of tabs"
[Name]="tab.name"
[TabCloseable]="tab.closeable"
[Visible]="tab.visible"
[ID]="tab.id">
{{tab.title}}
</mj-tab>
<mj-tab-body *ngFor="let tab of tabs">
<div class="tab-content">
<h3>{{tab.title}} Content</h3>
<p>{{tab.content}}</p>
<button (click)="updateTabContent(tab.id)">Update Content</button>
</div>
</mj-tab-body>
</mj-tabstrip>
</div>
`,
styles: [`
.tab-container {
height: 500px;
border: 1px solid #ccc;
padding: 10px;
}
.tab-content {
padding: 20px;
}
`]
})
export class DynamicTabsComponent {
selectedTabIndex = 0;
tabs = [
{
id: 1,
name: 'home',
title: 'Home',
content: 'Welcome to the home tab',
closeable: false,
visible: true
},
{
id: 2,
name: 'reports',
title: 'Reports',
content: 'View your reports here',
closeable: true,
visible: true
},
{
id: 3,
name: 'settings',
title: 'Settings',
content: 'Configure your settings',
closeable: true,
visible: true
}
];
nextTabId = 4;
addNewTab() {
const newTab = {
id: this.nextTabId++,
name: `tab-${Date.now()}`,
title: `New Tab ${this.nextTabId-1}`,
content: `This is the content for new tab ${this.nextTabId-1}`,
closeable: true,
visible: true
};
this.tabs.push(newTab);
// Need to wait for next tick to select the new tab
setTimeout(() => {
this.selectedTabIndex = this.tabs.length - 1;
});
}
updateTabContent(tabId: number) {
const tab = this.tabs.find(t => t.id === tabId);
if (tab) {
tab.content = `Updated content at ${new Date().toLocaleTimeString()}`;
}
}
onTabSelected(event: TabEvent) {
console.log('Tab selected:', event.index, event.tab?.Name);
}
onBeforeTabClosed(event: TabCancelableEvent) {
// Optionally prevent closing specific tabs
if (event.tab?.Name === 'important-tab') {
event.cancel = true;
alert('This tab cannot be closed!');
}
}
onTabClosed(event: TabClosedEvent) {
// Remove the tab from our array
const index = this.tabs.findIndex(t => t.id === event.tab?.ID);
if (index >= 0) {
this.tabs.splice(index, 1);
// Signal that we're done processing
event.done();
}
}
}
The main component that contains and manages the tabs.
SelectedTabIndex: number - The index of the selected tabFillWidth: boolean - Whether the tab strip should fill its container width (default: true)FillHeight: boolean - Whether the tab strip should fill its container height (default: true)ScrollAmount: number - Pixels to scroll when using scroll buttons (default: 150)BeforeTabSelected: EventEmitterTabSelected: EventEmitterBeforeTabClosed: EventEmitterTabClosed: EventEmitterTabContextMenu: EventEmitterTabScrolled: EventEmitterResizeContainer: EventEmitterSelectTabByName(tabName: string): MJTabComponent | undefined - Selects a tab by its name and returns the tab if foundGetTabByName(tabName: string): MJTabComponent | undefined - Gets a tab component by its nameCloseTab(tabIndex: number): Promise<void> - Closes a tab by its index (async)scrollLeft(): void - Scrolls the tab strip to the left by 150 pixels (note: currently ignores ScrollAmount input)scrollRight(): void - Scrolls the tab strip to the right by 150 pixels (note: currently ignores ScrollAmount input)scrollIntoView(tabIndex: number): void - Scrolls to make a specific tab visibleRefreshTabs(): void - Refreshes the tab strip after dynamic changesTabs: MJTabComponent[] - Array of tab componentsTabBodies: MJTabBodyComponent[] - Array of tab body componentsRepresents a tab header in the tab strip.
Name: string - The name of the tab (used for finding tabs by name)ID: any - Custom identifier for the tabProps: any - Additional properties to associate with the tabTabCloseable: boolean - Whether the tab can be closed (default: false)Visible: boolean - Whether the tab is visible (default: true)TabSelected: boolean - Whether the tab is selected (managed by the tab strip)selectTab(): void - Selects this tabcloseTab(event: MouseEvent): void - Closes this tabhandleContextMenu(event: MouseEvent): void - Handles context menu eventsTabStrip: MJTabStripComponent - Reference to the parent tab strip componentRepresents the content area for a tab.
TabVisible: boolean - Whether the tab body is visible (managed by the tab strip)FillWidth: boolean - Whether the tab body should fill its container width (default: true)FillHeight: boolean - Whether the tab body should fill its container height (default: true)Base event type for tab operations.
index: number - The index of the tabtab: MJTabComponent - Reference to the tab componentbody: MJTabBodyComponent - Reference to the tab body componentEvent type that allows cancellation of an operation.
cancel: boolean - Set to true to cancel the operationEvent type for tab closure.
newTabIndex: number - The suggested new tab index after closingdone: (error?: any) => {} - Callback that must be called when processing is completeEvent type for context menu events.
mouseEvent: MouseEvent - The original mouse eventYou can dynamically hide/show tabs while maintaining their state:
// Hide a tab
const tab = this.tabStrip.GetTabByName('settings');
if (tab) {
tab.Visible = false; // Tab will be hidden but not removed
}
// Show it again
tab.Visible = true;
Note: Hidden tabs cannot be selected. If a selected tab is hidden, the tab strip will automatically select the first visible tab.
@ViewChild('tabStrip') tabStrip!: MJTabStripComponent;
// Select a specific tab
this.tabStrip.SelectedTabIndex = 2;
// Or by name
this.tabStrip.SelectTabByName('reports');
// Close a tab programmatically
await this.tabStrip.CloseTab(1);
// Ensure a tab is visible in the viewport
this.tabStrip.scrollIntoView(5);
The component throws errors in these scenarios:
Always check for tab existence when working with dynamic tabs:
const tab = this.tabStrip.SelectTabByName('dynamicTab');
if (!tab) {
console.error('Tab not found');
}
The component includes basic CSS that can be customized to match your application's design. The tab strip uses Font Awesome icons for the scroll buttons (fa-caret-left and fa-caret-right).
.tabstrip-container - Main container element.tab-header-outer - Outer container for tab headers.tab-header-inner - Inner scrollable container for tabs.tab-scroll-button - Base class for scroll buttons.tab-scroll-button-left - Left scroll button.tab-scroll-button-right - Right scroll button.tab-bodies - Container for tab content@angular/common: ^18.0.2@angular/core: ^18.0.2@memberjunction/ng-container-directives: For container directivestslib: ^2.3.0This package uses Angular CLI for building:
npm run build
The package is configured with:
ngcThis tab strip component is designed to work seamlessly with other MemberJunction packages and follows the same patterns and conventions used throughout the MJ ecosystem. It's particularly useful in:
ISC