我怎样才能使MatDialog可拖动/角材料 [英] How can i make a MatDialog draggable / Angular Material

查看:164
本文介绍了我怎样才能使MatDialog可拖动/角材料的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使Angular Material对话框可拖动? 我安装了angular2-draggable,当然可以在所有其他元素上使用该功能.

Is it possible to make a Angular Material Dialog draggable? I installed angular2-draggable and can of course use the functionality on all other elements.

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

But because the dialogs are dynamically created i can not use ngDraggable on a special element or can use a template variable.

推荐答案

自Angular Material 7起更新

您可以简单地使用@angular/cdk/drag-drop

dialog.html

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

Stackblitz示例

Stackblitz Example

由于目前尚无官方解决方案,因此我将编写自定义指令,该指令将应用于对话框标题并为我们完成所有工作:

Since there is no official solution for that, I'm going to write custom directive that will be applied on a dialog title and do all job for us:

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运行示例

Ng-run Example

这里的基本思想是使用MatDialogRef.updatePosition方法更新对话框位置.在后台,此方法会更改margin-top | margin-left值,并且有人可以说这不是最佳选择,如果我们使用transform会更好,但我只是想举例说明如何不使用它技巧并借助内置服务.

The basic idea here is to use MatDialogRef.updatePosition method for updating dialog position. Under the hood this method changes margin-top|margin-left values and someone can argue that it's not the best option here and it would be better if we used transform but I simply want to show an example of how we can do it without some tricks and with the help of the built-in services.

我们还需要在指令中注入MatDialogContainer,以便获得对话框容器的初始位置.我们必须计算初始偏移量,因为Angular材质库使用flex居中对话框,并且无法获取特定的上/左值.

We also need to inject MatDialogContainer in our directive so that we can get initial position of dialog container. We have to calculate initial offset because Angular material library uses flex to center dialog and it doesn't get us specific top/left values.

dialog-draggable-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问:

Remember location

Since @Rolando asked:

我想记住"模态的位置,以便当 按下打开模态的按钮,模态在最后一次打开 位于".

I want to 'remember' where the modal was positioned so that when the button to open the modal is hit, the modal opens up where 'it was last located'.

尝试支持它.

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

In order to do that you can create some service where you will store dialog positions:

modal-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);
  }
}

现在您需要在我们的指令中注入此服务:

now you need to inject this service in our directive:

dialog-draggable-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));
  }

尽快关闭对话框,我保存了上一个偏移量.

As you can as soon as dialog is going to be closed i save last offset.

Ng运行示例

Ng-run Example

这种方式可以记住对话框的关闭位置

This way dialog remembers where it was closed

这篇关于我怎样才能使MatDialog可拖动/角材料的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆