我如何制作 MatDialog 可拖动/角度材质 [英] How can i make a MatDialog draggable / Angular Material

查看:36
本文介绍了我如何制作 MatDialog 可拖动/角度材质的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

解决方案

自 Angular Material 7 起更新

您可以简单地使用 @angular/cdk 中的

这里的基本思想是使用 MatDialogRef.updatePosition 方法更新对话框位置.在幕后,此方法更改了 margin-top|margin-left 值,有人可能会争辩说这不是这里的最佳选择,如果我们使用转换会更好,但我只是想展示一个示例,说明我们如何在没有一些的情况下做到这一点技巧和内置服务的帮助.

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

dialog-draggable-title.directive.ts

import { Directive, HostListener, OnInit } from '@angular/core';导入 { MatDialogContainer, MatDialogRef } from '@angular/material';从rxjs/订阅"导入{订阅};从 'rxjs/Observable' 导入 { Observable };从 'rxjs/operators/takeUntil' 导入 { takeUntil };导入 'rxjs/add/observable/fromEvent';从'rxjs/operators/take'导入{获取};@指示({选择器:'[mat-dialog-draggable-title]'})导出类 DialogDraggableTitleDirective 实现 OnInit {私人_subscription:订阅;鼠标开始:位置;mouseDelta:位置;偏移量:位置;构造函数(私有 matDialogRef: MatDialogRef

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.

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

解决方案

Update since Angular Material 7

You can simply use cdkDrag directive from @angular/cdk/drag-drop

dialog.html

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

Stackblitz Example

Previous answer:

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-run Example

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.

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

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'.

let's try to support it.

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-run Example

This way dialog remembers where it was closed

这篇关于我如何制作 MatDialog 可拖动/角度材质的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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