noxi雑記

.NET、Angularまわりの小ネタブログ

Angular CDK Overlay でポップアップ Directive を実装する

だいぶ昔の記事で Angular CDK の Overlay モジュールの使い方を解説しました 1 2 3
今回は標準機能では実装できない簡易ポップアップを表示する Directive を実装してみるメモです。



環境

Angular CLI: 12.1.0
Node: 14.17.1
Package Manager: npm 6.14.10
OS: win32 x64

Angular: 12.1.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, material, platform-browser, platform-browser-dynamic
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1201.0
@angular-devkit/build-angular   12.1.0
@angular-devkit/core            12.1.0
@angular-devkit/schematics      12.1.0
@schematics/angular             12.1.0
rxjs                            6.6.7
typescript                      4.3.4

実装内容

今回の実装では Overlay モジュールの CdkConnectedOverlay をマルッとコピペし、 FlexibleConnectedPositionStrategy に関する箇所を GlobalPositionStrategy に書き換えていきます。主には PositionStrategy に対する設定を行う _updatePositionStrategy の中身を書き換えることと、 offsetX などのプロパティの削除と追加になります。

元の CdkConnectedOverlayhttps://github.com/angular/components/blob/12.1.0/src/cdk/overlay/overlay-directives.ts

コピペで作り替えた AppGlobalOverlayhttps://github.com/noxi515/angular-sample-global-overlay-directive/blob/master/src/app/global-overlay/global-overlay.directive.ts

例えば _updatePositionStrategy はこのように変更しています。

  // Before
  private _updatePositionStrategy(positionStrategy: FlexibleConnectedPositionStrategy) {
    const positions: ConnectedPosition[] = this.positions.map(currentPosition => ({
      originX: currentPosition.originX,
      originY: currentPosition.originY,
      overlayX: currentPosition.overlayX,
      overlayY: currentPosition.overlayY,
      offsetX: currentPosition.offsetX || this.offsetX,
      offsetY: currentPosition.offsetY || this.offsetY,
      panelClass: currentPosition.panelClass || undefined,
    }));

    return positionStrategy
      .setOrigin(this.origin.elementRef)
      .withPositions(positions)
      .withFlexibleDimensions(this.flexibleDimensions)
      .withPush(this.push)
      .withGrowAfterOpen(this.growAfterOpen)
      .withViewportMargin(this.viewportMargin)
      .withLockedPosition(this.lockPosition)
      .withTransformOriginOn(this.transformOriginSelector);
  }

  // After
  private _updatePositionStrategy(positionStrategy: GlobalPositionStrategy) {
    if (this.centerVertical === true) {
      positionStrategy = positionStrategy.centerVertically(this.centerVerticalOffset);
    }
    if (this.centerHorizontal === true) {
      positionStrategy = positionStrategy.centerHorizontally(this.centerHorizontalOffset);
    }

    if (this.top != null) {
      positionStrategy = positionStrategy.top(this.top);
    }

    if (this.bottom != null) {
      positionStrategy = positionStrategy.bottom(this.bottom);
    }

    if (this.left != null) {
      positionStrategy = positionStrategy.left(this.left);
    }

    if (this.right != null) {
      positionStrategy = positionStrategy.right(this.right);
    }

    return positionStrategy;
  }

使い方

基本的に CdkConnectedOverlay と同一です。この実装で、ボタンを押すとカードのポップアップが出たり消えたりするようになります。

<button (click)="showPopup = !showPopup">Toggle popup</button>

<ng-template
  appGlobalOverlay
  [appGlobalOverlayOpen]="showPopup"
  [appGlobalOverlayWidth]="'200px'"
  [appGlobalOverlayHeight]="'100px'"
  [appGlobalOverlayCenterVertical]="true"
  [appGlobalOverlayCenterHorizontal]="true"
>
  <mat-card>
    <mat-card-title>ほげー</mat-card-title>
    <mat-card-content>ふがー</mat-card-content>
  </mat-card>
</ng-template>