是否可以使Angular Material对话框可拖动?我安装了angular2-draggable,并且当然可以在所有其他元素上使用该功能。
但是由于对话框是动态创建的,因此我无法将ngDraggable应用于特定元素或使用模板变量。
是否可以使Angular Material对话框可拖动?我安装了angular2-draggable,并且当然可以在所有其他元素上使用该功能。
但是由于对话框是动态创建的,因此我无法将ngDraggable应用于特定元素或使用模板变量。
你可以简单地使用来自@angular/cdk/drag-drop
的cdkDrag指令。
dialog.html
<h1 mat-dialog-title
cdkDrag
cdkDragRootElement=".cdk-overlay-pane"
cdkDragHandle>
Hi {{data.name}}
</h1>
由于没有官方的解决方案,我将编写一个自定义指令,将其应用于对话框标题并为我们完成所有工作:
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 {
这里的基本思路是使用 MatDialogRef.updatePosition
方法更新对话框位置。在底层,该方法会更改 margin-top|margin-left 值,有人可能会认为这不是最佳选项,最好使用 transform,但我只是想展示一下我们如何利用内置服务而不使用某些技巧来完成它。
我们还需要在指令中注入 MatDialogContainer,以便可以获取对话框容器的初始位置。我们必须计算初始偏移量,因为 Angular Material 库使用 flex 来居中对话框,并且它不会给我们特定的 top/left 值。
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的要求:
我希望“记住”模态框的位置,这样当单击打开模态框的按钮时,模态框会在“上次定位”的位置打开。
我们尝试支持它。
为此,您可以创建一个服务来存储对话框位置:
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);
}
}
现在你需要将此服务注入到我们的指令中:
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));
}
当对话框即将关闭时,我会保存最后的偏移量。
这样对话框就记住了它关闭时的位置。
ngAfterViewInit()
中设置偏移量,而不是 ngOnInit()
- 现在它工作正常! - TmTron在你的模块中,导入CDK Drag模块。
import { DragDropModule } from '@angular/cdk/drag-drop';
在HTML中,例如对话框,只需添加到任何HTML元素。我已经添加到第一个元素,然后我可以将对话框拖动到任何我选择的位置。
<mat-dialog-content cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle>
content...
</mat-dialog-content>
this.dialog.open
时,您需要指定要打开的组件,在该组件中添加这些指令,并且将其作为示例发布。 - pinarelladialog.open
API方法添加可拖动效果的最简单方式 - undefined如果其他人也遇到了这个问题,在这里使用cdkDrag和cdkDragHandle在同一个元素上实际上是无效的。相关的GH问题可以在这里找到:
angular2-draggable
中,您可以使用ngDraggable
使元素可拖动。
其中ngDraggable
是一个指令,在您的情况下,您需要将此ngDraggable
指令动态地附加到您动态创建的对话框上。