Developing a Tab component
- The consumer should use it like this:
<tabs>
<tab tabTitle="Tab 1">
Here's some content.
</tab>
<tab tabTitle="Tab 2">
And here's more in another tab.
</tab>
</tabs>
Create tabs
component
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.scss']
})
export class TabsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
</ul>
- Use the component with
my-tabs
tag
<my-tabs></my-tabs>
We need a place to show the tab content with ng-content
- Add ng-content in the template
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
</ul>
<ng-content></ng-content>
- When using the component, put tab contents in
<tabs>
tag
<my-tabs>
<p>Tab content</p>
</my-tabs>
Create a tab element to replace <li>Tab 1</li>
with the property tabTitle
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'my-tab',
templateUrl: './tab.component.html',
styleUrls: ['./tab.component.scss']
})
export class TabComponent implements OnInit {
@Input() tabTitle: string;
constructor() { }
ngOnInit() {
}
}
<div>
<ng-content></ng-content>
</div>
- Use the component like this:
<my-tabs>
<my-tab tabTitle="tab1">
Tab 1 content
</my-tab>
<my-tab tabTitle="tab2">
Tab 2 content
</my-tab>
</my-tabs>
Generate tabs dynamic
- Using
*ngFor
directive to generate li element
<ul>
<li *ngFor="let tab of tabs"></li>
</ul>
- Create
tabs
property in TabsComponent, get children component withContentChildren
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
And
how to test
component with ViewChildren - Create test component in the spec file as the host
@Component({
selector: 'test-my-tabs',
template: ''
})
export class TestTabsComponent {
constructor() { }
}
- Create html template in spec file
const html = `
<my-tabs>
<my-tab tabTitle="tab1">
Tab 1 content
</my-tab>
<my-tab tabTitle="tab2">
Tab 2 content
</my-tab>
</my-tabs>
`;
- Init Angular test component with the
TestTabsComponent
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestTabsComponent, TabsComponent, TabComponent]
});
}));
beforeEach(() => {
TestBed.overrideComponent(TestTabsComponent, { set: { template: html } });
fixture = TestBed.createComponent(TestTabsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
- Override the component template with
TestBed.overrideComponent
before creating the test component anddo not compile the component before override
beforeEach(() => {
TestBed.overrideComponent(TabsComponent, { set: { template: html } });
fixture = TestBed.createComponent(TabsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
- Then the children components will be created
it('should display childrens components with the property tabs', () => {
const tabs = fixture.debugElement.queryAll(By.css('li'));
expect(tabs.length).toBe(2);
});
- And we can get
TabsComponent instance
with@ViewChild
@Component({
selector: 'test-my-tabs',
template: ''
})
export class TestTabsComponent {
@ViewChild(TabsComponent) tabs: TabsComponent;
constructor() { }
}
it('should active the first tab as default', () => {
const tabsComponent = component.tabs;
expect(tabsComponent.tabs.first.active).toBeTruthy();
expect(tabsComponent.tabs.last.active).toBeFalsy();
});
Switch the tab content when clicking the tab title
Test first
it('should siwtch tab when clicking the title', () => {
const tabTitles = fixture.debugElement.queryAll(By.css('li'));
const secondTabTitle = tabTitles[tabTitles.length - 1].nativeElement;
secondTabTitle.click();
expect(component.tabs.tabs.first.active).toBeFalsy('the first tab is unactive');
expect(component.tabs.tabs.last.active).toBeTruthy('the tab clicked is active');
});
Then bind click event to selectTab method
<li *ngFor="let tab of tabs" (click)="selectTab(tab)">
</li>
selectTab(tab: TabComponent) {
this.tabs.forEach(x => x.active = false);
tab.active = true;
}