Angular 客制弹出视窗

因为个人的 blog 需要一个弹出视窗做提示,而套件里的样式我都不喜欢,所以今天就来客制化一个吧!

这次的弹出视窗会使用 Angular Material 里的 cdk 来做开发


前置作业

安装 cdk

npm i @angular/cdk

注意: 若发现 @angular/cdk 里的某些功能不能挂载,可以看一下 @angular/cdk@angular/core 的版本是否一样,若一直报错不能使用的话,就安装成同个版本号处理试试 (当时一直踩到这个雷)。

styles.scss 引入 css

@import "~@angular/cdk/overlay-prebuilt.css";

app.module.ts 里 引入 OverlayModule

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";

import { AppComponent } from "./app.component";
import { OverlayModule } from "@angular/cdk/overlay";

@NgModule({
  imports: [BrowserModule, FormsModule, OverlayModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {}

开始

template

<button (click)="onClick()" type="button">Open</button>

<ng-template #tpl>
  <div class="modal">
    <i class="modal__close" (click)="onClose()">X</i>
    <div class="modal__container">HI 我是弹出视窗内容</div>
  </div>
</ng-template>

建立 ng-template 这个是弹出视窗,让点 button 的时後呼叫。
可以看看前几天写的这篇文 Angular ElementRef、TemplateRef、viewContainerRef 的区别

.ts

import { Overlay, OverlayConfig, OverlayRef } from "@angular/cdk/overlay";
import { TemplatePortal } from "@angular/cdk/portal";
import {
  Component,
  ElementRef,
  OnInit,
  TemplateRef,
  ViewChild,
  ViewContainerRef,
} from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
  @ViewChild("tpl") tplRef!: TemplateRef<any>;
  overlayRef!: OverlayRef;

  constructor(
    private overlay: Overlay,
    private viewContainerRef: ViewContainerRef
  ) {}

  ngOnInit(): void {
    // 设定弹窗出来时的定位
    const strategy = this.overlay
      .position()
      .global()
      .centerHorizontally()
      .centerVertically();

    const configs = new OverlayConfig({
      hasBackdrop: true,
      positionStrategy: strategy,
    });

    this.overlayRef = this.overlay.create(configs);
    this.overlayRef.backdropClick().subscribe((res) => {
      this.overlayRef.detach();
    });
  }

  onClick() {
    this.overlayRef.attach(
      new TemplatePortal(this.tplRef, this.viewContainerRef)
    );
  }

  onClose() {
    this.overlayRef.detach();
  }
}

弹窗定位方式

弹窗定位:OverlayPositionBuilder

文件上写的没有很清楚

这里笔记一下

原点定位 (中心点以 button 为主) https://material.angular.tw/cdk/overlay/api#ConnectedPosition

在 constructor 要再多引入 ElementRef

const strategy = this.overlay
  .position()
  .flexibleConnectedTo(this.elementRef)
  .withPositions([
    {
      originX: "start",
      originY: "bottom",
      overlayX: "start",
      overlayY: "top",
    },
  ]);

全域定位 (中心点以整个画面为主) https://material.angular.tw/cdk/overlay/api#GlobalPositionStrategy

const strategy = this.overlay
  .position()
  .global()
  .centerHorizontally()
  .centerVertically();

弹窗设定

const configs = new OverlayConfig({
  hasBackdrop: true,
  positionStrategy: strategy,
});

这里我只设定了两种,

更多设定方式可以看文件 OverlayConfig


打开弹窗

onClick() {
  this.overlayRef.attach(
    new TemplatePortal(this.tplRef, this.viewContainerRef)
  );
}

建立浮动图层 OverlayRef

关闭弹窗

onClose() {
  this.overlayRef.detach();
}

范例:https://stackblitz.com/edit/angular-ivy-ujphmp


後记

今天介绍 cdk 的 overlay 的基本应用,在实作上会再将这个弹窗提出,处理成共用元件,有需要的话直接引入使用就可以罗


<<:  Day 19 - UML x Component — Independent (上)

>>:  [Day19] Vue 3 单元测试 (Unit Testing) - Event Handling

MIS 要交接那些工作事项?

年节过後,又到转职的季节。这两天,又有小朋友来问:「MIS工作要交接那些事项?」 这几乎算是每年的&...

网页三巨头:HTML、CSS、JavaScript的关联性

建立一个网页需要三个技术HTML、CSS、JavaScript缺一不可!! 今天来介绍这三个技术有何...

Eloquent ORM - 一对多关联

接着要来给 Todo 加上与 User 的关联,区分各 User 建立的 Todo。 一个 User...

数据治理(Data Governance)

在数据治理程序中,有一些常见的角色,例如数据所有者,数据管理员,数据保管人等。数据所有者应对其拥有...

[Day 12] SRE - 定期演练计画

灾害模拟演练 今天跟各位分享一下,我们团队从零开始的定期演练流程。 事前准备 先开个google试算...