Skip to content

Instantly share code, notes, and snippets.

@wellwind
Last active December 11, 2017 01:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wellwind/95218f7d976d6f20b181a33bf6ef730d to your computer and use it in GitHub Desktop.
Save wellwind/95218f7d976d6f20b181a33bf6ef730d to your computer and use it in GitHub Desktop.
CDK Practice

Service

InteractivityChecker

export class AppComponent implements OnInit, AfterContentInit {
  @ViewChild('button') button: ElementRef;

  constructor(private interactivityChecker: InteractivityChecker) {}

  ngOnInit() {
    console.log(this.interactivityChecker.isDisabled(this.button.nativeElement));
    console.log(this.interactivityChecker.isFocusable(this.button.nativeElement));
    console.log(this.interactivityChecker.isTabbable(this.button.nativeElement));
    console.log(this.interactivityChecker.isVisible(this.button.nativeElement));
  }
}

LiveAnnouncer

打開VoiceOver看看,目前確定朗讀程式沒反應@@

  say() {
    this.liveAnnouncer.announce('hello~~', 'polite');
  }

FocusMonitor

  startMonitor() {
    this.monitorLog += `<<< start monitoring element\n`;
    this.focusMonitor.monitor(this.container.nativeElement, this.renderer2, false).subscribe(mode => {
      this.monitorLog += `element focused by ${mode}\n`;
    });
  }

  stopMonitor() {
    this.focusMonitor.stopMonitoring(this.container.nativeElement);
    this.monitorLog += `>>> stop monitoring element\n`;
  }

Classes

ListKeyManager

export class AppComponent implements OnInit, AfterViewInit, AfterViewChecked {
  // 找出所有的MatButton
  @ViewChildren(MatButton) buttons: QueryList<MatButton>;
  listKeyManager: FocusKeyManager<MatButton>;
  
  @HostListener('keydown', ['$event'])
  keydown($event: KeyboardEvent) {
    // 監聽鍵盤事件並依照AS移動按鈕focus狀態
    if ($event.code === 'KeyA') {
      this.listKeyManager.setPreviousItemActive();
    } else if ($event.code === 'KeyS') {
      this.listKeyManager.setNextItemActive();
    }
  }

  ngAfterViewInit() {
    // 建立FocusKeyManager, withWrap的話, 會形成一個focus的範圍區段
    this.listKeyManager = new FocusKeyManager(this.buttons).withWrap();
    // active到最後一個
    this.listKeyManager.setLastItemActive();
  }
}

Directives

FocusTrap

<button (click)="display = !display" #button>TEST</button>
<div cdkTrapFocus cdkTrapFocusAutoCapture="true" *ngIf="display">
  <input type="text" cdkFocusRegionStart>
  <input type="text" >
  <input type="text" cdkFocusInitial>
  <input type="text" >
  <input type="text" cdkFocusRegionEnd>
</div>

cdkMonitorElementFocus / cdkMonitorSubtreeFocus

  • 可以減少使用 FocusMonitor
  • focus時會加上class: cdk-focused
  • 依照focuse不同的類型會加上不同class
    • cdk-mouse-focused
    • cdk-keyboard-focused
    • cdk-program-focused
    • cdk-touch-focused
  • 注意需要tabindex=?
<div class="demo-focusable" tabindex="0" cdkMonitorElementFocus>
  <p>div with element focus monitored</p>
  <button>1</button><button>2</button>
</div>

<div class="demo-focusable" tabindex="0" cdkMonitorSubtreeFocus>
  <p>div with subtree focus monitored</p>
  <button>1</button><button>2</button>
</div>

ObserveContent

當Component內資料變更時,可以收到通知

<div class="projected-content-wrapper" (cdkObserveContent)="projectContentChanged($event)">
  <ng-content></ng-content>
</div>
<div>資料變更第: {{ count }} 次</div>
export class CdkObservableDemoComponent implements OnInit {
  count = 0;
  constructor() {}

  ngOnInit() {}

  projectContentChanged(event: MutationRecord[]) {
    console.log(event);
    ++this.count;
  }
}

Usage

<app-cdk-observable-demo>
  {{ count }}
</app-cdk-observable-demo>

<button (click)="count = count + 1">Count</button>
<ng-template #menu let-name="name" let-nickname="nickname">
  <mat-nav-list class="mat-elevation-z2 mat-menu-panel">
    <h3 matSubheader>Hi {{ name }} ({{ nickname }})</h3>
    <mat-divider></mat-divider>
    <a mat-list-item routerLink>Link</a>
    <a mat-list-item routerLink>Initially focused</a>
    <a mat-list-item routerLink>Focus region end</a>
    <a mat-list-item routerLink>Link</a>
  </mat-nav-list>
</ng-template>
<button mat-fab (click)="showMenu()" cdkOverlayOrigin style="position: fixed; bottom: 15px; right: 15px"><mat-icon>add</mat-icon></button>
<button class="mat-raised-button" (click)="showModalMenu()">Show Modal Menu</button>
export class AppComponent {
  @ViewChild('menu') menu: TemplateRef<any>;
  @ViewChild(CdkOverlayOrigin) buttonOrigin: CdkOverlayOrigin;
  constructor(
    public overlay: Overlay,
    public viewContainerRef: ViewContainerRef) {}
    
  showMenu() {
    if (this.viewContainerRef.length > 0) {
      this.viewContainerRef.clear();
    } else {
      const strategy = this.overlay
        .position()
        .connectedTo(this.buttonOrigin.elementRef, { originX: 'end', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' });

      const config = new OverlayConfig({
        hasBackdrop: true,
        backdropClass: 'cdk-overlay-transparent-backdrop', // 沒有設這個的話, 會有一個灰色背景檔在後面
        positionStrategy: strategy,
        scrollStrategy: this.overlay.scrollStrategies.reposition() // scroll後會重新定位
        // noop: 不做任何事情(預設), close: 關閉, block: 禁止捲動, reposition: 重新定位
      });
      const overlayRef = this.overlay.create(config);

      overlayRef.attach(new TemplatePortal(this.menu, this.viewContainerRef, {name: 'Mike', nickname: 'AA'}));
      overlayRef.backdropClick().subscribe(() => overlayRef.detach());
    }
  }
  
  showModalMenu() {
    const strategy = this.overlay
      .position()
      .global()
      // .top('100px')
      // .left('200px')
      .centerHorizontally()
      .centerVertically()
      .width('500px')
      .height('500px');

    const config = new OverlayConfig({
      hasBackdrop: true,
      positionStrategy: strategy,
      scrollStrategy: this.overlay.scrollStrategies.block() // 關掉卷軸
    });
    const overlayRef = this.overlay.create(config);

    overlayRef.attach(new ComponentPortal(MyListComponent, this.viewContainerRef));
    overlayRef.backdropClick().subscribe(() => overlayRef.detach());
  }
 }
<h2> The portal outlet is here: </h2>
<div class="demo-portal-outlet">
  <ng-template [cdkPortalOutlet]="selectedPortal"></ng-template>
</div>

<button type="button" (click)="selectedPortal = programmingJoke">
  Programming joke
</button>

<button type="button" (click)="selectedPortal = mathJoke">
  Math joke
</button>

<button type="button" (click)="selectedPortal = scienceJoke">
  Science joke
</button>

<!-- Template vars on <ng-template> elements can't be accessed _in_ the template because Angular
    doesn't support grabbing the instance / TemplateRef this way because the variable may be
    referring to something *in* the template (such as #item in ngFor). As such, the component
    has to use @ViewChild / @ViewChildren to get these references.
    See https://github.com/angular/angular/issues/7158 -->
<ng-template cdk-portal>
  <p> - Why don't jokes work in octal?</p>
  <p> - Because 7 10 11.</p>
</ng-template>

<div *cdk-portal>
 <p> - Did you hear about this year's Fibonacci Conference? </p>
 <p> - It's going to be as big as the last two put together. </p>
</div>
/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {ComponentPortal, Portal, CdkPortal} from '@angular/cdk/portal';
import {Component, QueryList, ViewChildren} from '@angular/core';


@Component({
  moduleId: module.id,
  selector: 'portal-demo',
  templateUrl: 'portal-demo.html',
  styleUrls: ['portal-demo.css'],
})
export class PortalDemo {
  @ViewChildren(CdkPortal) templatePortals: QueryList<Portal<any>>;

  selectedPortal: Portal<any>;

  get programmingJoke() {
    return this.templatePortals.first;
  }

  get mathJoke() {
    return this.templatePortals.last;
  }

  get scienceJoke() {
    return new ComponentPortal(ScienceJoke);
  }
}


@Component({
  moduleId: module.id,
  selector: 'science-joke',
  template: `<p> 100 kilopascals go into a bar. </p>`
})
export class ScienceJoke { }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment