Angular CDK Overlay 弹出覆盖物

为什么使用Overlay?Overlay中文翻译过来意思是覆盖物,它是MaterialDesigncomponentsforAngular中针对弹出动态内容这一场景的封装,功能强大、使用方便,尤其在开发自己的组件库时,可以让你少写许多代码,可以说只要是弹出内容的场景基本都可以使用Overlay.我们自己的组件库中弹出场景基本都已经使用Overlay,如自定义Select、Cascader、Tree...

Angular CDK Overlay 弹出覆盖物

为什么使用Overlay?

Overlay中文翻译过来意思是覆盖物,它是Material Design components for Angular中针对弹出动态内容这一场景的封装,功能强大、使用方便,尤其在开发自己的组件库时,可以让你少写许多代码,可以说只要是弹出内容的场景基本都可以使用Overlay.
我们自己的组件库中弹出场景基本都已经使用Overlay,如自定义Select、Cascader、Tree Select、Tooltip、Dialog等,总结最重要的的两点好处:

  1. 让使用者不再进行繁琐的位置计算,而简单通过参数配置就实现内容的定位,而且关于位置的各种情况都有考虑到.
  2. 组件的弹出内容都是用Overlay实现,避免了各自实现的产生的不兼容,如相互遮盖问题.

简单示例 - 连结位置源的弹出

下面通过一个示例代码来展示Overlay的使用,这种弹出场景类似于Tooltip,弹出的overlay内容是基于一个参照的位置源origin元素.

安装并且导入模块

项目中如果没有安装CDK,要先安装

  npm install @angular/cdk
导入OverlayModule
import {OverlayModule} from '@angular/cdk/overlay';@NgModule({  imports: [ OverlayModule, // ...  ]})export class AppModule {}
示例模板内容
<div class="demo-trigger">  <!--触发位置源-->  <button mat-raised-buttoncdkOverlayOrigintype="button"[disabled]="overlayRef"(click)="openWithConfig()">Open</button></div><!--弹出动态内容模板--><ng-template #overlay>  <div class="demo-overlay"> <div ><ul><li *ngFor="let item of itemArray; index as i">{{itemText}} {{i}}</li></ul> </div>  </div></ng-template>

除了弹出模板,上面模板中还有一个Open按钮,后面要用到它作为位置源origin

注入Overlay服务

在组件的constructor构造函数中注入Overlay服务,下面代码包括组件的定义

@Component({  selector: 'overlay-demo',  templateUrl: 'connected-overlay-demo.html'})export class ConnectedOverlayDemo {  @ViewChild(CdkOverlayOrigin, {static: false}) _overlayOrigin: CdkOverlayOrigin;  @ViewChild('overlay', {static: false}) overlayTemplate: TemplateRef<any>;  /*** 注入Overlay服务*/  constructor(public overlay: Overlay) { } openWithConfig() {  }}

处理注入服务,上面代码还通过ViewChild取到模板中的两个对象,后面用到的时候再解释.

构建位置策略

首先创建一个位置策略,这里使用的是FlexibleConnectedPositionStrategy策略,先看代码

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

创建FlexibleConnectedPositionStrategy策略的方法flexibleConnectedTo必须要提供一个位置源参数,这里使用的是
this._overlayOrigin.elementRef,弹出内容的位置是基于这个位置源的,this._overlayOrigin其实就是通过ViewChild取的模板中的Open按钮.

调用创建方法
 this.overlayRef = this.overlay.create({positionStrategy, // 位置策略scrollStrategy: this.overlay.scrollStrategies.reposition(), // 滚动策略direction: this.dir.value, // 可用性方面的设置,不用太关注minWidth: 200, // overlay层的最小宽度minHeight: 50 // overlay层的最小高度hasBackdrop: false // 是否显示遮罩层 });

方法会生成一个OverlayRef类型的对象overlayRef,用overlayRef来管理Overlay。

通过overlayRef附加模板

一切准备就绪后,这里就是需要告诉Overlay弹出层要显示的内容,直接弹出模板,在模板中定义,这里用到的是overlayRef的attach方法,代码如下

 this.overlayRef.attach(new TemplatePortal(this.overlayTemplate, this.viewContainerRef));

代码中用到了this.overlayTemplate,通过ViewChild取到的显示弹出内容的模板定义.

注:attach方法用到了CDK里面的Protals,attach方法接收的参数类型其实是TemplatePortal,因为这个说到底是动态创建组件,除此之外它还支持组件类型的ComponentPortal,关于Portals可以参考我前面的文章https://zhuanlan.zhihu.com/p/59719621

通过以上简单的几个步骤就实现动态内容的弹出,效果图如下所示

屏幕录制 2019-06-11 下午10.30.13.2019-06-11 22_38_52.gif

简单示例 - 全局弹出

与上面的完全不同,现在介绍下通过Overlay直接弹出内容在窗口上,不连结任何位置源,非常简单只需要更改下位置策略,看下使用的新位置策略的代码

 const positionStrategy = this.overlay.position().global().height('300px').centerHorizontally().top('70px');

调用global()返回的是全局的位置策略GlobalPositionStrategy,基于浏览器窗口绝对定位的位置策略。
以上代码实现:水平居中,距离顶部70px,效果图如下

屏幕录制 2019-06-11 下午10.44.31.2019-06-11 22_47_32.gif

Overlay 位置策略

Overlay通过OverlayPositionBuilder服务提供了三个方法分别对应三种位置策略,OverlayPositionBuilder通过构造函数注入到了Overlay服务中,前面代码this.overlay.position()返回的就是OverlayPositionBuilder类型的对象

ConnectedPositionStrategy - 连结点位置策略

注:该策略已被弃用,使用FlexibleConnectedPositionStrategy策略代替,但是这里的讨论可以继续,用以说明连接点的位置关系

connectedTo方法返回ConnectedPositionStrategy策略实例,该策略实现基于一个操作源上的位置点到Overlay弹出层的位置点连接关系的位置策略,创建策略的代码如下

  connectedTo(elementRef: ElementRef,originPos: OriginConnectionPosition,overlayPos: OverlayConnectionPosition): ConnectedPositionStrategy { return new ConnectedPositionStrategy(  originPos, overlayPos, elementRef, this._viewportRuler, this._document, this._platform,  this._overlayContainer);  }

参数分别是:

  1. elementRef要连接的元素,一般是触发弹出的元素
  2. originPos 连接元素的位置点
  3. overlayPos overlay的位置点

用图表达它所维系的位置关系

image.png

上图所示的位置点组合只是其中一种情况(左下点 - 左上点),位置配置代码如下

 {  "originX": "start",  "originY": "bottom",  "overlayX": "start",  "overlayY": "top"}

在x方向上可枚举值定义如下

 export type HorizontalConnectionPos = 'start' | 'center' | 'end';

在y方向上可枚举值定义

 export type VerticalConnectionPos = 'top' | 'center' | 'bottom';

基于以上枚举值可以实现各种位置组合。

FlexibleConnectedPositionStrategy - 灵活的连接点位置策略

注:现在的源代码ConnectedPositionStrategy策略最终也是通过关联FlexibleConnectedPositionStrategy策略实现的,所以推荐直接使用该策略

通过flexibleConnectedTo方法返回FlexibleConnectedPositionStrategy策略实例,这是Overlay最复杂的一个位置策略,所以能称上Flexible,通过指令方式使用Overlay时就是使用的这个策略,它在位置策略上有更多的控制,特性如下:

  1. withDefaultOffsetXwithDefaultOffsetY设置相对基础位置的偏移。
  2. withPositions参数为ConnectionPositionPair类型的数组,提供多种位置组合,当某一种位置组合的弹出内容超出窗口,就会应用对应其它的位置组合来避免内容不可见。
  3. withFlexibleDimensions控制Overlay弹出层宽度和高度是否被限制在浏览器窗口内,参数设置为ture时,宽度和高度会自适应到浏览器边界,以滚动条形式展现内容。
  4. 等等位置方面其它可预见的细节的处理

创建策略的代码如下

/*** Creates a flexible position strategy.* @param origin Origin relative to which to position the overlay.*/  flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy { return new FlexibleConnectedPositionStrategy(origin, this._viewportRuler, this._document,  this._platfor
源文地址:https://www.guoxiongfei.cn/cntech/19287.html