如何制作MatDialog可拖动/角度材质

时间:2017-11-27 12:20:59

标签: angular dialog draggable material

是否可以使Angular Material Dialog可拖动? 我安装了angular2-draggable,当然可以使用所有其他元素的功能。

但是因为对话框是动态创建的,所以我不能在特殊元素上使用ngDraggable,也不能使用模板变量。

4 个答案:

答案 0 :(得分:35)

自Angular Material 7

以来的更新

您只需使用@angular/cdk/drag-drop

中的cdkDrag指令即可

<强> dialog.html

<h1 mat-dialog-title 
   cdkDrag
   cdkDragRootElement=".cdk-overlay-pane" 
   cdkDragHandle>
     Hi {{data.name}}
</h1>

<强> Stackblitz Example

上一个答案:

由于没有官方解决方案,我将编写将应用于对话标题的自定义指令并为我们完成所有工作:

<强> dialog.html

@Component({
  selector: 'app-simple-dialog',
  template: `
    <h1 mat-dialog-title mat-dialog-draggable-title>Hi {{data.name}}</h1>
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    <div mat-dialog-content>
      ...
    </div>
    <div mat-dialog-actions>
      ...
    </div>
  `
})
export class SimpleDialogComponent {

<强> Ng-run Example

enter image description here

这里的基本思想是使用MatDialogRef.updatePosition方法更新对话框位置。在引擎盖下,这种方法改变了边缘 - 边缘 - 左边的值,有人可以说这不是最好的选择,如果我们使用变换会更好但我只是想展示一个如何在没有一些的情况下做到这一点的例子技巧和内置服务的帮助。

我们还需要在我们的指令中注入MatDialogContainer,以便我们可以获得对话框容器的初始位置。我们必须计算初始偏移量,因为Angular材质库使用flex to center对话框,它不会为我们提供特定的顶部/左侧值。

<强>对话框的拖动-title.directive.ts

import { Directive, HostListener, OnInit } from '@angular/core';
import { MatDialogContainer, MatDialogRef } from '@angular/material';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
import { takeUntil } from 'rxjs/operators/takeUntil';
import 'rxjs/add/observable/fromEvent';
import { take } from 'rxjs/operators/take';

@Directive({
  selector: '[mat-dialog-draggable-title]'
})
export class DialogDraggableTitleDirective implements OnInit {

  private _subscription: Subscription;

  mouseStart: Position;

  mouseDelta: Position;

  offset: Position;

  constructor(
    private matDialogRef: MatDialogRef<any>,
    private container: MatDialogContainer) {}

  ngOnInit() {
    this.offset = this._getOffset();
  }

  @HostListener('mousedown', ['$event'])
  onMouseDown(event: MouseEvent) {
    this.mouseStart = {x: event.pageX, y: event.pageY};

    const mouseup$ = Observable.fromEvent(document, 'mouseup');
    this._subscription = mouseup$.subscribe(() => this.onMouseup());

    const mousemove$ = Observable.fromEvent(document, 'mousemove')
      .pipe(takeUntil(mouseup$))
      .subscribe((e: MouseEvent) => this.onMouseMove(e));

    this._subscription.add(mousemove$);
  }

  onMouseMove(event: MouseEvent) {
      this.mouseDelta = {x: (event.pageX - this.mouseStart.x), y: (event.pageY - this.mouseStart.y)};

      this._updatePosition(this.offset.y + this.mouseDelta.y, this.offset.x + this.mouseDelta.x);
  }

  onMouseup() {
    if (this._subscription) {
      this._subscription.unsubscribe();
      this._subscription = undefined;
    }

    if (this.mouseDelta) {
      this.offset.x += this.mouseDelta.x;
      this.offset.y += this.mouseDelta.y;
    }
  }

  private _updatePosition(top: number, left: number) {
    this.matDialogRef.updatePosition({
      top: top + 'px',
      left: left + 'px'
    });
  }

  private _getOffset(): Position {
    const box = this.container['_elementRef'].nativeElement.getBoundingClientRect();
    return {
      x: box.left + pageXOffset,
      y: box.top + pageYOffset
    };
  }
}


export interface Position {
  x: number;
  y: number;
}

记住位置

自@Rolando问道:

  

我想'记住'模态的位置,以便当时   打开模态的按钮被击中,模态打开了它最后的位置   位于”。

让我们试着支持它。

为此,您可以创建一些存储对话位置的服务:

<强>模态-position.cache.ts

@Injectable()
export class ModalPositionCache {
  private _cache = new Map<Type<any>, Position>();

  set(dialog: Type<any>, position: Position) {
    this._cache.set(dialog, position);
  }

  get(dialog: Type<any>): Position|null {
    return this._cache.get(dialog);
  }
}

现在你需要在我们的指令中注入这个服务:

<强>对话框的拖动-title.directive.ts

export class DialogDraggableTitleDirective implements OnInit {
  ...

  constructor(
    private matDialogRef: MatDialogRef<any>,
    private container: MatDialogContainer,
    private positionCache: ModalPositionCache
  ) {}

  ngOnInit() {
    const dialogType = this.matDialogRef.componentInstance.constructor;
    const cachedValue = this.positionCache.get(dialogType);
    this.offset = cachedValue || this._getOffset();
    this._updatePosition(this.offset.y, this.offset.x);

    this.matDialogRef.beforeClose().pipe(take(1))
      .subscribe(() => this.positionCache.set(dialogType, this.offset));
  }

一旦对话框关闭,我就可以保存最后的偏移量。

<强> Ng-run Example

这种方式对话框会记住它关闭的位置

enter image description here

答案 1 :(得分:4)

在您的模块中,导入cdk拖动

import { DragDropModule } from '@angular/cdk/drag-drop';

,例如在其中有对话框的html中,只需将其添加到任何html元素中即可。我已添加到第一个元素,然后可以将对话框拖动到我选择的任何位置。

<mat-dialog-content cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle>
  content...
</mat-dialog-content>

答案 2 :(得分:1)

万一其他人碰到了这个问题,实际上对在同一元素上使用cdkDrag和cdkDragHandle就像在这里的示例中所做的那样。相关的GH问题可以在这里找到:

https://github.com/angular/components/issues/18984

答案 3 :(得分:0)

<{1>}中的

,您使用angular2-draggable使元素可拖动。 其中ngDraggable是一个指令,在您的情况下,您需要动态地附加此ngDraggable指令与动态创建的对话框。

虽然正式,但没有办法动态添加指令,但在以下问题中已经讨论了一些肮脏的技巧来动态添加指令。

How to dynamically add a directive?

Use Angular2 Directive in host of another Directive