Angular cdk 学习之 Accessibility(ally)

       cdk ally里面提供的功能运用场景是 select, menu 这种有 list options 的组件, 负责处理键盘上下按钮时 option active 的逻辑。cdk Accessibility 部分官方文档连接https://material.angular.io/cdk/a11y/overview

       和cdk里面其他功能模块一样,cdk ally使用之前也要provider导入A11yModule模块。

import {A11yModule} from '@angular/cdk/a11y';

一 ListKeyManager

       ListKeyManager主要做的事情就是去管理一堆的list 里面哪个item处于active状态(active代表一种状态,比如处于活动转换,获取焦点状态等等)。而且ListKeyManager管理的item必须实现ListKeyManagerOption接口。换句话说ListKeyManager用于管理一系列list的item,这些item就是ListKeyManagerOption。通过ListKeyManager来控制那个ListKeyManagerOption对应的item处于active状态。所以咱们先看下ListKeyManagerOption和ListKeyManager里面一些常见的方法,如下:

1.1 ListKeyManager常用方法

       讲ListKeyManager方法的时候咱们顺带讲下ListKeyManagerOption里面的方法

1.1.1 ListKeyManagerOption常用方法介绍

export interface ListKeyManagerOption {
    /** 当前item是否disabled. */
    disabled?: boolean;
    /** 获取当前item对应的label,配合ListKeyManager里面withTypeAhead函数的使用
     *  适用于输入框的情况,比如输入框下面有一些list item。当输入框输入字符的时候list item 里面的active
     *  item 和输入的文字匹配
     */
    getLabel?(): string;
}

1.1.2 ListKeyManager常用方法介绍

export declare class ListKeyManager<T extends ListKeyManagerOption> {
    /**
     * tab 按键的时候触发的
     */
    tabOut: Subject<void>;
    /** 当ListKeyManager里面的list item ,active item 改变的时候触发*/
    change: Subject<number>;
    /**
     * 设置那些ListKeyManager管理的list里在移动(比如tab 按键切换)过程中那些item需要跳过。
     */
    skipPredicate(predicate: (item: T) => boolean): this;
    /**
     * 设置是否循环移动(当active item是最好一个的时候,继续下一个跳到第一个)
     */
    withWrap(shouldWrap?: boolean): this;
    /**
     * active item 移动方向垂直(对应键盘方向键的 上下按键)
     */
    withVerticalOrientation(enabled?: boolean): this;
    /**
     * active item 移动方向水平(对应键盘方向键的 左右按键)
     */
    withHorizontalOrientation(direction: 'ltr' | 'rtl' | null): this;
    /**
     * 用来处理组合按键的情况,比如 altr + 方向键的时候。同样达到方向按键的效果
     */
    withAllowedModifierKeys(keys: ListKeyManagerModifierKey[]): this;
    /**
     * 设置typeahead 模式 配合ListKeyManagerOption的getLabel()函数使用,一般用于配合有输入框的情况下使用,比如输入框输入一个字符,active item 会自动设置到包含这个字符item
     */
    withTypeAhead(debounceInterval?: number): this;
    /**
     * 设置 active item 对应的index
     */
    setActiveItem(index: number): void;
    /**
     * 设置 active item 对应的item
     */
    setActiveItem(item: T): void;
    /**
     * 设置按键
     */
    onKeydown(event: KeyboardEvent): void;
    /** 当前active item 对应的事件 */
    readonly activeItemIndex: number | null;
    /** 当前active item 对应的item */
    readonly activeItem: T | null;
    /** 设置第一个位置的item 为active item(当然了如果第一个item 是disable,则设置第二个) */
    setFirstItemActive(): void;
    /** 设置最后一个位置的item 为 active item */
    setLastItemActive(): void;
    /** 设置下一个item 为active item */
    setNextItemActive(): void;
    /** 设置上一个item 为active item */
    setPreviousItemActive(): void;
    /**
     * 设置active item,但是不产生其他的效果
     */
    updateActiveItem(index: number): void;
    /**
     * 设置active item,但是不产生其他的效果
     */
    updateActiveItem(item: T): void;
}

1.2 ListKeyManager的使用

       接下来咱们通过ListKeyManager的两个具体继承类ActiveDescendantKeyManager和FocusKeyManager来说明ListKeyManager的具体使用。其实ActiveDescendantKeyManager里面管理的item必须实现Highlightable接口、FocusKeyManager里面管理的item必须实现FocusableOption。

1.2.1 ActiveDescendantKeyManager的使用

       ActiveDescendantKeyManager里面管理的list item 必须实现Highlightable接口。

ActiveDescendantKeyManager继承了ListKeyManager类,Highlightable实现了ListKeyManagerOption接口

       ActiveDescendantKeyManager使用场景:如果想ListKeyManager管理的list里面active的item标记为活动状态,其他的标记为非活动状态情况下使用。而且每个item都必须实现Highlightable接口。接下来咱们通过一个具体的实例来简单的说明下。

咱们自定义一个item组件ItemActiveOptionComponent,这个组件实现Highlightable接口。组件里面做的事情也很简单就是去改变active item的class样式。

import {Component, HostBinding, Input} from '@angular/core';
import {Highlightable} from "@angular/cdk/a11y";

@Component({
    selector: 'app-item-active-option',
    template: `
        <div [class.disabled]="disabled">
            <ng-content></ng-content>
        </div>
    `,
    styles: [`
        .active {
            background-color: lightblue;
            color: #fff;
        }

        .disabled {
            opacity: 0.3;
        }
    `]
})
export class ItemActiveOptionComponent implements Highlightable {
    @Input() item;
    @Input() disabled = false;
    private _isActive = false;

    @HostBinding('class.active') get isActive() {
        return this._isActive;
    }

    /**
     * 设置 active对应的class
     */
    setActiveStyles() {
        this._isActive = true;
    }

    /**
     * 设置非active对应的class
     */
    setInactiveStyles() {
        this._isActive = false;
    }

    getLabel() {
        return this.item.name;
    }
}


cdk-active-descendant.component.less文件,组件对应样式

.form-control {
  display: block;
  width: 100%;
  height: calc(2.25rem + 2px);
  padding: .375rem .75rem;
  font-size: 1rem;
  line-height: 1.5;
  color: #495057;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid #ced4da;
  border-radius: .25rem;
  transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}

.form-control:focus {
  color: #495057;
  background-color: #fff;
  border-color: #80bdff;
  outline: 0;
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}


.list-group-item:first-child {
  margin-top: 1rem;
  border-top-left-radius: .25rem;
  border-top-right-radius: .25rem;
}

.list-group-item  {
  position: relative;
  display: block;
  padding: .75rem 1.25rem;
  margin-bottom: -1px;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, .125);
}

.list-group-item:last-child {
  margin-bottom: 0;
  border-bottom-right-radius: .25rem;
  border-bottom-left-radius: .25rem;
}

.list-group-item.active  {
  z-index: 2;
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

ActiveDescendantKeyManager的使用,咱们放在这个组件里面实现。ActiveDescendantKeyManager里面管理的item就是咱们上面自定义的ItemActiveOptionComponent。具体的实现可以看下代码。

import {AfterViewInit, Component, QueryList, ViewChildren} from '@angular/core';
import {ItemActiveOptionComponent} from "./active-item-option/item-active-option.component";
import {ActiveDescendantKeyManager} from "@angular/cdk/a11y";

@Component({
    selector: 'app-cdk-active-descendant',
    template: `
        <!-- 输入框 -->
        <div class="form-group">
            <input class="form-control" placeholder="Search..." (keyup)="onKeyDown($event)" #input>
        </div>

        <section class="list-group">
            <!-- ActiveDescendantKeyManager要管理的item -->
            <app-item-active-option *ngFor="let user of users | filter: 'name' : input.value; index as index"
                                    [item]="user" class="list-group-item">
                {{user.name}}
            </app-item-active-option>
        </section>
    `,
    styleUrls: ['cdk-active-descendant.component.less']
})
export class CdkActiveDescendantComponent implements AfterViewInit {

    /**
     * 找到所有的item(ListKeyManagerOption - Highlightable)
     */
    @ViewChildren(ItemActiveOptionComponent) items: QueryList<ItemActiveOptionComponent>;
    // list item source list
    users = [
        {
            "id": "5b902934d965e7501f4e1c6f",
            "name": "Caroline Hodges"
        },
        {
            "id": "5b9029348f7eed8b6f5f02db",
            "name": "Delores Rivas"
        },
        {
            "id": "5b9029346f48c8407c64d0d5",
            "name": "Darlene Franklin"
        },
        {
            "id": "5b9029341eff315fa87f9e21",
            "name": "Alfreda Love"
        },
        {
            "id": "5b9029342e8917c6ccdb9865",
            "name": "Marcy Ratliff"
        },
        {
            "id": "5b9029349dbb48013460e01f",
            "name": "Beulah Nielsen"
        },
        {
            "id": "5b902934f4f1586e5e72d74a",
            "name": "Morton Kerr"
        },
        {
            "id": "5b9029347918bb204bf7014e",
            "name": "Autumn Tillman"
        },
        {
            "id": "5b902934b86f80e1fc60c626",
            "name": "Diane Bennett"
        },
        {
            "id": "5b9029348999f59215020349",
            "name": "June Eaton"
        }
    ];

    private keyManager: ActiveDescendantKeyManager<ItemActiveOptionComponent>;

    ngAfterViewInit() {
        // new ActiveDescendantKeyManager
        this.keyManager = new ActiveDescendantKeyManager(this.items)
            .withWrap() // 循环
            .withTypeAhead(); // 支持搜索

    }

    onKeyDown(event) {
        // 传递事件进去
        this.keyManager.onKeydown(event);
    }
}

ActiveDescendantKeyManager.gif

1.2.2 FocusKeyManager的使用

       FocusKeyManager里面管理的item必须实现FocusableOption接口。

FocusKeyManager继承了ListKeyManager类,FocusableOption实现了ListKeyManagerOption接口

       FocusKeyManager使用场景:如果想ListKeyManager管理的list里面active item想直接接受到浏览器的焦点focus的时候使用。每个item都必须实现FocusableOption接口。

自定义一个组件ItemFocusOptionComponent,并且这个组件实现了FocusableOption接口,FocusableOption接口的focus()方法里面设置当前item获取焦点。

import {Component, ElementRef, HostBinding, Input} from '@angular/core';
import {FocusableOption, FocusOrigin} from "@angular/cdk/a11y";

@Component({
    selector: 'app-item-focus-option',
    template: `
        <ng-content></ng-content>
    `,
    styles: [
            `:host:focus {
            background: lightblue;
            color: #fff;
        }`
    ]
})

export class ItemFocusOptionComponent implements FocusableOption {
    @Input() item;

    /**
     * 屏蔽掉默认的键盘事件,js里面自己控制键盘事件
     */
    @HostBinding('tabindex') tabindex = '-1';

    constructor(private host: ElementRef) {
    }

    getLabel() {
        return this.item.name;
    }

    /**
     * 设置获取焦点
     */
    focus(origin?: FocusOrigin): void {
        this.host.nativeElement.focus();
    }
}

cdk-focus.component.less 样式文件

.form-control {
  display: block;
  width: 100%;
  height: calc(2.25rem + 2px);
  padding: .375rem .75rem;
  font-size: 1rem;
  line-height: 1.5;
  color: #495057;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid #ced4da;
  border-radius: .25rem;
  transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}

.form-control:focus {
  color: #495057;
  background-color: #fff;
  border-color: #80bdff;
  outline: 0;
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}


.list-group-item:first-child {
  margin-top: 1rem;
  border-top-left-radius: .25rem;
  border-top-right-radius: .25rem;
}

.list-group-item  {
  position: relative;
  display: block;
  padding: .75rem 1.25rem;
  margin-bottom: -1px;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, .125);
}

.list-group-item:last-child {
  margin-bottom: 0;
  border-bottom-right-radius: .25rem;
  border-bottom-left-radius: .25rem;
}

.list-group-item.focus {
  z-index: 2;
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}

FocusKeyManager的具体使用

import {AfterViewInit, Component, QueryList, ViewChildren} from '@angular/core';
import {FocusableOption, FocusKeyManager} from "@angular/cdk/a11y";
import {ItemFocusOptionComponent} from "./focus-item-option/item-focus-option.component";

@Component({
    selector: 'app-cdk-focus',
    template: `
        <section class="list-group" (keyup)="onKeyDown($event)">
            <app-item-focus-option *ngFor="let user of users; index as index"
                                   [item]="user" class="list-group-item"
                                   (click)="selectItem(index)">
                {{user.name}}
            </app-item-focus-option>
        </section>
    `,
    styleUrls: ['./cdk-focus.component.less']
})
export class CdkFocusComponent implements AfterViewInit {

    // 获取搜有实现了FocusableOption接口的item
    @ViewChildren(ItemFocusOptionComponent) items: QueryList<ItemFocusOptionComponent>;
    // list source
    users = [
        {
            "id": "5b902934d965e7501f4e1c6f",
            "name": "Caroline Hodges"
        },
        {
            "id": "5b9029348f7eed8b6f5f02db",
            "name": "Delores Rivas"
        },
        {
            "id": "5b9029346f48c8407c64d0d5",
            "name": "Darlene Franklin"
        },
        {
            "id": "5b9029341eff315fa87f9e21",
            "name": "Alfreda Love"
        },
        {
            "id": "5b9029342e8917c6ccdb9865",
            "name": "Marcy Ratliff"
        },
        {
            "id": "5b9029349dbb48013460e01f",
            "name": "Beulah Nielsen"
        },
        {
            "id": "5b902934f4f1586e5e72d74a",
            "name": "Morton Kerr"
        },
        {
            "id": "5b9029347918bb204bf7014e",
            "name": "Autumn Tillman"
        },
        {
            "id": "5b902934b86f80e1fc60c626",
            "name": "Diane Bennett"
        },
        {
            "id": "5b9029348999f59215020349",
            "name": "June Eaton"
        }
    ];

    private keyManager: FocusKeyManager<ItemFocusOptionComponent>;

    ngAfterViewInit() {
        // 创建FocusKeyManager对象
        this.keyManager = new FocusKeyManager(this.items)
            .withWrap()
            .withTypeAhead();

    }

    /**
     * 传递按键事件
     * @param event
     */
    onKeyDown(event) {
        this.keyManager.onKeydown(event);
    }

    /**
     * 点击选中
     * @param index
     */
    selectItem(index: number) {
        this.keyManager.setActiveItem(index);
    }

}

FocusKeyManager.gif

二 FocusTrap

       FocusTrap是cdk ally模块里面提供的一个指令。用于捕获元素中的Tab键焦点。同时控制焦点的范围,Tab切换焦点的时候不会跳出这个区域。举个例子比如有一个视图元素添加了FocusTrap指令,这个视图元素里面有三个input。这样Tab键切换焦点的时候焦点会在这三个input之间切换。

       Selector: [cdkTrapFocus]

       Exported as: cdkTrapFocus

       还有几个其他的指令可以配合FocusTrap指令来使用:cdkFocusRegionStart、cdkFocusRegionEnd、cdkFocusInitial。咱们上面不是说了FocusTrap指令会控制焦点的范围。cdkFocusRegionStart和cdkFocusRegionEnd指令有可以进一步的控制焦点的范围

  • cdkFocusRegionStart:FocusTrap范围的起点
  • cdkFocusRegionEnd:FocusTrap范围的终点
  • cdkFocusInitial:区间出现时,一开始获取focus的item。

2.1 FocusTrap指令属性介绍

FocusTrap属性 类型 解释
autoCapture: boolean @Input('cdkTrapFocusAutoCapture') 初始化的时候元素是否自动获取焦点
enabled: boolean @Input('cdkTrapFocus') false Tab切换焦点的时候会跑到外面去,true Tab切换焦点的时候焦点不会跑到外面去

2.2 FocusTrap指令使用

import {Component} from '@angular/core';

@Component({
    selector: 'app-cdk-accessibility',
    template: `
        <!-- FocusTrap的使用,- 控制焦点的范围,tab切换焦点的时候不会跳出这个区域 -->
        <div cdkTrapFocus style="border: solid 1px #ccc; padding: 10px">
            <!-- Tab and Shift + Tab will not leave this element. -->
            <input placeholder="Email">
            <input type="password" placeholder="Password" style="margin-left: 10px">
            <button type="submit" style="margin-left: 10px">Submit</button>
        </div>
    `
})
export class CdkAccessibilityComponent {
}

2.3 cdkFocusRegionStart cdkFocusRegionEnd cdkFocusInitial的使用

       cdkFocusRegionStart和cdkFocusRegionEnd的使用应该很好理解。就是用来控制范围的。关键是cdkFocusInitial的使用,cdkFocusInitial必须要配合cdkTrapFocus指令的cdkTrapFocusAutoCapture=true使用。而且我试了下cdkFocusInitial一开始是没有效果,没都是隐藏显示的时候才有效果,才获取到焦点。

import {Component} from '@angular/core';

@Component({
    selector: 'app-cdk-accessibility',
    template: `
        <div *ngIf="displayFocusTrap" cdkTrapFocus cdkTrapFocusAutoCapture="true"
             style="border: solid 1px #ccc; padding: 10px; margin-top: 10px">
            <input value="1">
            <input style="margin-left: 10px" value="2" cdkFocusRegionStart>
            <input style="margin-left: 10px" value="3" cdkFocusInitial>
            <input style="margin-left: 10px" value="4" cdkFocusRegionEnd>
        </div>
        <button (click)="displayFocusTrap=!displayFocusTrap">显示</button>
    `
})
export class CdkAccessibilityComponent {

    displayFocusTrap = false;

}



三 FocusMonitor

       FocusMonitor是cdk ally里面提供的一个Service。用于监控焦点。FocusMonitor里面常用方法如下

export declare class FocusMonitor implements OnDestroy {

    /**
     * 监听元素的焦点获取
     */
    monitor(element: HTMLElement, checkChildren?: boolean): Observable<FocusOrigin>;
    monitor(element: ElementRef<HTMLElement>, checkChildren?: boolean): Observable<FocusOrigin>;
    /**
     * 停止监听元素的焦点获取
     */
    stopMonitoring(element: HTMLElement): void;
    stopMonitoring(element: ElementRef<HTMLElement>): void;
    /**
     * 设置元素获取焦点
     * @param element 元素
     * @param origin 设置的焦点是通过哪个来的 'touch' | 'mouse' | 'keyboard' | 'program' | null
     * @param options 用于配置focus行为的选项.
     */
    focusVia(element: HTMLElement, origin: FocusOrigin, options?: FocusOptions): void;
    focusVia(element: ElementRef<HTMLElement>, origin: FocusOrigin, options?: FocusOptions): void;
}

3.1 FocusMonitor的使用

       一个简单的例子,咱们仅仅是在元素获取焦点的时候做一个简单的打印。

import {AfterViewInit, Component, ElementRef, OnDestroy} from '@angular/core';
import {FocusMonitor} from "@angular/cdk/a11y";

@Component({
    selector: 'app-cdk-focus-monitor',
    template: `
        <div cdkTrapFocus style="border: solid 1px #ccc; padding: 10px; margin-top: 10px">
            <input value="FocusMonitor item 1">
            <input style="margin-left: 10px" value="FocusMonitor item 2">
            <input style="margin-left: 10px" value="FocusMonitor item 3">
            <input style="margin-left: 10px" value="FocusMonitor item 4">
        </div>
    `
})
export class CdkFocusMonitorComponent implements AfterViewInit, OnDestroy {

    constructor(private _elementRef: ElementRef,
                private _focusMonitor: FocusMonitor) {
    }

    ngAfterViewInit() {
        /**
         * 这里我们只是做了一个简单的打印
         */
        this._focusMonitor.monitor(this._elementRef.nativeElement, true).subscribe(mode => {
            console.log('元素获取到焦点 focused 来源 ' + mode);
        });
    }

    ngOnDestroy() {
        this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
    }

}

四 FocusTrapFactory

       FocusTrapFactory也是cdk ally里面提供的一个Service。他的功能就是用来给元素添加cdkFocusTrap指令。FocusTrapFactory常用函数就一个,如下

export declare class FocusTrapFactory {
    /**
     * 给指定的元素添加cdkFocusTrap指令,deferCaptureElements参数正好对应cdkFocusTrap指令
     * @Input('cdkTrapFocusAutoCapture')功能
     */
    create(element: HTMLElement, deferCaptureElements?: boolean): FocusTrap;
}

4.1 FocusTrapFactory使用

       通过FocusTrapFactory的crete函数达到cdkFocusTrap指令的效果。

import {AfterViewInit, Component, ElementRef} from '@angular/core';
import {FocusTrap, FocusTrapFactory} from "@angular/cdk/a11y";

@Component({
    selector: 'app-cdk-focus-trap-factory',
    template: `
        <div style="border: solid 1px #ccc; padding: 10px; margin-top: 10px">
            <input value="FocusTrapFactory item 1">
            <input style="margin-left: 10px" value="FocusTrapFactory item 2">
            <input style="margin-left: 10px" value="FocusTrapFactory item 3">
            <input style="margin-left: 10px" value="FocusTrapFactory item 4">
        </div>
    `
})
export class CdkFocusTrapFactoryComponent implements AfterViewInit {

    private _focusTrap: FocusTrap;

    constructor(private _elementRef: ElementRef,
                private _focusTrapFactory: FocusTrapFactory) {
    }

    ngAfterViewInit() {
        this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
    }

}

五 InteractivityChecker

       InteractivityChecker是一个Service,用于检查元素的一些状态。常见方法如下。

export declare class InteractivityChecker {
    /**
     * 元素是否 disabled.
     */
    isDisabled(element: HTMLElement): boolean;
    /**
     * 元素是否visible
     *
     * This will capture states like `display: none` and `visibility: hidden`, but not things like
     * being clipped by an `overflow: hidden` parent or being outside the viewport.
     *
     * @returns Whether the element is visible.
     */
    isVisible(element: HTMLElement): boolean;
    /**
     * 元素是否接受Tab按键,不如Tab按键切换焦点
     */
    isTabbable(element: HTMLElement): boolean;
    /**
     * 元素是否可以获取焦点
     */
    isFocusable(element: HTMLElement): boolean;
}

5.1 InteractivityChecker使用

       举一个非常简单的例子,打印出button的各种状态。

import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {InteractivityChecker} from "@angular/cdk/a11y";

@Component({
    selector: 'app-cdk-interactivity-checker',
    template: `
        <button #interactivityCheckerButton>InteractivityChecker测试</button>
        <p>上面button是否disable: {{disable}}</p>
        <p>上面button是否visible: {{visible}}</p>
        <p>上面button是否可以tabable: {{tabable}}</p>
        <p>上面button是否可以focusable: {{focusable}}</p>
    `
})
export class CdkInteractivityCheckerComponent implements OnInit {

    @ViewChild('interactivityCheckerButton') button: ElementRef;
    disable: boolean;
    visible: boolean;
    tabable: boolean;
    focusable: boolean;

    constructor(private _interactivityChecker: InteractivityChecker) {
    }

    ngOnInit() {
        this.disable = this._interactivityChecker.isDisabled(this.button.nativeElement);
        this.visible = this._interactivityChecker.isVisible(this.button.nativeElement);
        this.tabable = this._interactivityChecker.isTabbable(this.button.nativeElement);
        this.focusable = this._interactivityChecker.isFocusable(this.button.nativeElement);
    }

}

六 LiveAnnouncer

       LiveAnnouncer也是一个Service。用于在屏幕上发布一个消息。把消息显示在屏幕上。关于这一部分的内容估计的去看下W3C关于WAI-ARIA的使用。我们就举一个简单的例子。

import {Component} from '@angular/core';
import {LiveAnnouncer} from "@angular/cdk/a11y";

@Component({
    selector: 'app-cdk-live-announcer',
    template: `
    `
})
export class CdkLiveAnnouncerComponent {

    index = 1;

    /**
     * 会在屏幕上输出Hey Google,三秒之后又会换成Hey Google 2
     */
    constructor(private liveAnnouncer: LiveAnnouncer) {
        liveAnnouncer.announce("Hey Google");
        setTimeout(() => {
            this.timerTask();
        }, 3000);
    }

    timerTask() {
        this.index = this.index + 1;
        this.liveAnnouncer.announce("Hey Google " + this.index.toString(), "off");
    }

}


       关于cdk ally咱们就扯这么多,希望能对大家有点帮助。当然了里面很有很多其他的类咱们没有介绍到。大家在时机使用的时候可以对照官方文档看下 https://material.angular.io/cdk/a11y/overview。 文章中涉及到的例子连接地址 https://github.com/tuacy/angular-cdk-study

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容

  • # 传智播客vue 学习## 1. 什么是 Vue.js* Vue 开发手机 APP 需要借助于 Weex* Vu...
    再见天才阅读 3,518评论 0 6
  • 第一节:初识Angular-CLI第二节:登录组件的构建第三节:建立一个待办事项应用第四节:进化!模块化你的应用第...
    接灰的电子产品阅读 13,668评论 64 25
  • Angualr drag-drop里面的功能能让我们非常方便的处理页面上视图的拖拽(自由拖拽、列表排序拖拽、...
    tuacy阅读 7,970评论 0 2
  • 前段时间,看了一段西瓜视频。讲述了一个农村家庭,参加,变形记的小女孩儿。名字叫什么我就不发了,我们就把她叫小花吧。...
    钟容阅读 289评论 0 0
  • 文/荷曦 居民楼内星星点点的灯光让人感觉温馨,可能,有些人还在计划这个长假去哪玩,也有人在抓紧完成欠下的稿子。不管...
    荷曦阅读 452评论 4 3