使用Angular Component Dev Kit创建简单的下拉菜单

首先

在Angular Material中,我发现没有像菜单和自动完成这样的可编辑内容的下拉菜单,因此我将使用Component Dev Kit (CDK)的Overlay模块创建一个可以编辑内容的下拉菜单模块。

目标

达成目标

实现目标

完成目标

追求目标

スクリーンショット 2019-07-10 17.47.57.png

准备

安装Angular和CDK。关于NodeJS略过。

$ npm install -g @angular/cli
$ ng new cdk-overlay-trial
$ cd cdk-overlay-trial/
$ npm install @angular/cdk
$ ng version

...

Angular CLI: 8.1.0
Node: 11.15.0
OS: darwin x64
Angular: 8.1.0

...

导入所需的模块。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { OverlayModule } from '@angular/cdk/overlay'; // 追加
import { PortalModule } from '@angular/cdk/portal'; // 追加

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    OverlayModule, // 追加
    PortalModule // 追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

记得不要忘记CSS。

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

实施 – 制作外观

我会先从外观开始写。将模板引用变量”dropdownContent”传递给”appDropdownToggle”属性。”appDropdownToggle”是一个属性指令。将下拉菜单的模板传递给这个指令,并在其中执行显示下拉菜单的操作。

“app-dropdown-content”被指定为一个模板引用变量,用于组件,为了显示Overlay,将其提前进行portal化。

<button [appDropdownToggle]="dropdownContent">show dropdown</button>
<app-dropdown-content #dropdownContent>
  <div>some content</div>
</app-dropdown-content>

我们将创建前文中所述的组件和指令。

dropdown-content : Overlay表示となるドロップダウンの本体です。

dropdown-trigger : ドロップダウンの表示/非表示を制御するディレクティブです。

$ ng generate component dropdown-content
$ ng generate directive dropdown-content/dropdown-toggle

我们可以使用创建的DropdownToggleDirective来接收模板引用变量。

import { Directive, Input } from '@angular/core';
import { DropdownContentComponent } from './dropdown-content.component';
...
export class DropdownToggleDirective {

  @Input('appDropdownToggle') dropdownContentRef: DropdownContentComponent;
...
}
スクリーンショット 2019-07-09 18.14.33.png
<ng-template cdk-portal>
  <div class="dropdown-wrapper">
    <ng-content></ng-content>
  </div>
</ng-template>

顺便设置样式,让下拉菜单看起来更合适。
如果你使用 Angular Material,我认为你可以使用 elevation helpers 来替代 box-shadow 部分。

.dropdown-wrapper {
  background-color: #FFF;
  border-radius: 4px;
  box-shadow: 0 2px 4px -1px rgba(0,0,0,.2), 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12);
}

实施-显示下拉菜单

显示Overlay

我們將使用DropdownToggleDirective來觸發。
我們將對showDropdown()方法添加@Hostlistener裝飾器。當按下按鈕時,將調用此方法以顯示下拉菜單。
另外,我們也應該創建一個hideDropdown()方法,用於隱藏下拉菜單。

export class DropdownToggleDirective {
...

  @HostListener('click') showDropdown() {
  }

  hideDropdown() {
  }

}

为了将实现的作为DropdownComponent的子元素的元素显示在下拉菜单中,我将确保DropdownToggleDirective可以引用它。

由于已经持有DropdownComponent的引用,所以只需要在@ViewChild中将其放置即可。

import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
...
export class DropdownContentComponent implements OnInit {

  @ViewChild(TemplateRef, {static: false}) templateRef: TemplateRef<any>;
...
}

我们将在DropdownToggleDirective中添加显示下拉菜单的处理。创建Overlay容器,创建模板的实例并将实例附加到容器中。(不能确定这种理解或表达是否正确)

import { Directive, Input, HostListener, ViewContainerRef } from '@angular/core';
import { DropdownContentComponent } from './dropdown-content.component';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
...
export class DropdownToggleDirective {
...
  overlayRef: OverlayRef;

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

  @HostListener('click') showDropdown() {

    this.overlayRef = this.overlay.create();

    const templatePortal = new TemplatePortal(this.dropdownContentRef.templateRef, this.viewContainerRef);

    this.overlayRef.attach(templatePortal);

  }
...
}

我想,大致上当你点击按钮时会出现一个下拉菜单(或类似的东西)。

调整Overlay的位置

我会添加用于调整位置的设置。随后,还会添加其他不涉及位置调整的设置。
我会创建一个OverlayConfig,并在调用this.overlay.create()时传递并应用它。

import { Directive, Input, HostListener, ViewContainerRef, ElementRef } from '@angular/core';
import { Overlay, OverlayRef, OverlayConfig } from '@angular/cdk/overlay';
...
  constructor(
    private overlay: Overlay,
    private viewContainerRef: ViewContainerRef,
    private viewElement: ElementRef
  ) { }

  @HostListener('click') showDropdown() {

    const config = this._generateOverlayConfig();

    this.overlayRef = this.overlay.create(config);
...
  }

  private _generateOverlayConfig(): OverlayConfig {

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

    return {
      positionStrategy: overlayPosition
    };
  }
...

让我们稍微详细地讨论一下位置调整。

使用`.flexibleConnectedTo(this.viewElement)`来设置Overlay位置的参考点。其中`this.viewElement`是指该指令所在的DOM的引用。也就是说,在这种情况下,将使用button元素作为Overlay位置的参考点。

除了`.flexibleConnectedTo()`之外,还有`.global()`。正如其名称所示,它将位置的参考点设置为全局,即以整个窗口作为参考点来确定位置。当创建覆盖整个屏幕的模态框时,可以使用此选项。

使用.withPositions()来设置具体位置。可以指定的值如下:

    • originX, overlayX: ‘start’ | ‘end’ | ‘center’

 

    originY, overlayY: ‘top’ | ‘bottom’ | ‘center’

除了上述之外,还有offsetX和offsetY,并且似乎可以进行微调。对于所设置的值会有什么效果,真的不是很清楚…我稍后会进行验证。虽然文件上有说明,但我可能理解不够…

隐藏覆盖层

如果保持当前的状态,无论点击下拉菜单做什么都无法隐藏它,所以我们将使其在点击除下拉菜单之外的元素时隐藏。

将hasBackdrop设为true可以阻止点击背面元素的操作等。另外,通过单独设置backdropClass,可以避免默认CSS样式被应用到背景层。如果不进行此设置,背景层会显示为灰色。

  private _generateOverlayConfig(): OverlayConfig {
...
    return {
      positionStrategy: overlayPosition,
      hasBackdrop: true,
      backdropClass: 'cdk-overlay-transparent-backdrop'
    };
  }

添加一个处理来隐藏Dropdown的Overlay。然后,在附加了portal时,设置点击背景时隐藏。


  @HostListener('click') showDropdown() {
...
    this.overlayRef.attach(templatePortal);

    this.overlayRef.backdropClick().subscribe(() => this.hideDropdown());

  }
...
  hideDropdown() {
    this.overlayRef.dispose();
  }

}

只需要对下拉菜单中的元素进行随意的样式设置,它就会看起来不错。

<button [appDropdownToggle]="dropdownContent">show dropdown</button>
<app-dropdown-content #dropdownContent>
  <div style="padding: 8px">some content</div>
</app-dropdown-content>

结束 (Jie shu)

通过琢磨,我终于做出了一个可以多功能复用的下拉菜单。
正如 @Quramy 所说,这个样本真的很少。我自己查看了 Angular Material 的测试代码来确认使用方法。

希望这些功能能够更广泛地普及,因为它们非常实用。(在使用AngularJS时,我经常为那些隐藏在背后的下拉菜单感到困扰…)

请参考

    • Angular Component Dev Kit 入門 – https://qiita.com/Quramy/items/caf015e56d536411d4ba

API reference for Angular CDK overlay – https://material.angular.io/cdk/overlay/api

How to create a custom dropdown using Angular CDK – http://prideparrot.com/blog/archive/2019/3/how_to_create_custom_dropdown_cdk

广告
将在 10 秒后关闭
bannerAds