深入指南
Angular Aria

Tabs

概述

分頁以分層方式顯示內容區段,一次只顯示一個面板。使用者點選分頁按鈕,或用方向鍵導覽分頁清單,就能在面板之間切換。

用法

分頁很適合將相關內容組織成不同區段,讓使用者在不同檢視或分類之間切換。

適合使用分頁的情況:

  • 將相關內容組織成不同區段
  • 建立具有多個分類的設定面板
  • 建立具有多個主題的文件
  • 實作具有不同檢視的儀表板
  • 顯示需要使用者切換情境的內容

避免使用分頁的情況:

  • 建立循序表單或精靈(改用 stepper 模式)
  • 在頁面之間導覽(改用路由導覽)
  • 顯示單一內容區段(不需要分頁)
  • 分頁超過 7-8 個(考慮改用其他版面配置)

功能

  • 選取模式:分頁可在取得焦點時自動啟用,或需要手動啟用
  • 鍵盤導覽:用方向鍵、Home 與 End 有效地導覽分頁
  • 方向:水平或垂直的分頁清單版面配置
  • 延遲內容:分頁面板只在啟用時才算繪
  • 停用分頁:停用個別分頁,並管理焦點
  • 焦點模式:roving tabindex 或 activedescendant 焦點策略
  • RTL 支援:支援由右至左語言的導覽

範例

選取跟隨焦點

當選取跟隨焦點時,你用方向鍵導覽,分頁就會立即啟用。這能提供即時回饋,很適合輕量的內容。

在分頁清單上設定 [selectionMode]="'follow'",即可啟用這個行為。

手動啟用

採用手動啟用時,方向鍵只會在分頁之間移動焦點,不會改變選取的分頁。使用者需按 Space 或 Enter 來啟用取得焦點的分頁。

對於內容較重的面板,使用 [selectionMode]="'explicit'",可避免不必要的算繪。

垂直分頁

將分頁垂直排列,適用於設定面板或導覽側邊欄等介面。

在分頁清單上設定 [orientation]="'vertical'"。導覽會改用上/下方向鍵。

延遲算繪內容

在 ng-template 上使用 ngTabContent 指令,可延後算繪分頁面板,直到面板顯示時才算繪。

<div ngTabs>
  <ul ngTabList [(selectedTab)]="selectedTab">
    <li ngTab value="tab1">Tab 1</li>
    <li ngTab value="tab2">Tab 2</li>
  </ul>

  <div ngTabPanel value="tab1">
    <ng-template ngTabContent>
      <!-- This content only renders when Tab 1 is shown -->
      <app-heavy-component />
    </ng-template>
  </div>

  <div ngTabPanel value="tab2">
    <ng-template ngTabContent>
      <!-- This content only renders when Tab 2 is shown -->
      <app-another-component />
    </ng-template>
  </div>
</div>

依預設,面板隱藏時,內容會從 DOM 中移除。將 [preserveContent]="true" 設為保留內容,例如面板中含有切換分頁時不應重置的影片時。

停用分頁

停用個別分頁,以防止使用者互動。可控制停用分頁是否能取得鍵盤焦點。

當分頁清單的 [softDisabled]="true" 時,停用分頁可以取得焦點,但無法啟用。當 [softDisabled]="false" 時,鍵盤導覽會跳過停用分頁。

測試

Angular Aria 提供元件測試工具(component harness)來測試分頁元件。 以下範例示範如何在元件測試中使用這些測試工具:

import {ComponentFixture, TestBed} from '@angular/core/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {ComponentHarness, HarnessLoader} from '@angular/cdk/testing';
import {TabsHarness} from '@angular/aria/tabs/testing';
import {MyTabsComponent} from './my-tabs'; // Your component

// A simple harness to help query content inside the tab panel
class TestContentHarness extends ComponentHarness {
  static hostSelector = '.test-content';
  async getText(): Promise<string> {
    return (await this.host()).text();
  }
}

describe('MyTabsComponent', () => {
  let fixture: ComponentFixture<MyTabsComponent>;
  let loader: HarnessLoader;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      imports: [MyTabsComponent],
    });

    fixture = TestBed.createComponent(MyTabsComponent);
    await fixture.whenStable();
    loader = TestbedHarnessEnvironment.loader(fixture);
  });

  it('should switch tabs and scope panel queries', async () => {
    const tabs = await loader.getHarness(TabsHarness);

    // Get all tabs
    const tabItems = await tabs.getTabs();
    expect(tabItems.length).toBe(3);

    // Verify initial selection
    expect(await tabItems[0].isSelected()).toBe(true);
    expect(await tabItems[1].isSelected()).toBe(false);

    // Query content inside the active tab panel
    // TabHarness automatically scopes queries to its associated panel
    const content = await tabItems[0].getHarness(TestContentHarness);
    expect(await content.getText()).toBe('Content 1');

    // Switch to the second tab
    await tabItems[1].select();

    // Verify selection updated
    expect(await tabItems[0].isSelected()).toBe(false);
    expect(await tabItems[1].isSelected()).toBe(true);
  });
});

API 參考

如需詳細的 API 文件,請查看下列 API 參考: