Comment puis-je faire un MatDialog draggable / Matériau angulaire
est-il possible de faire un dialogue de matériau anguleux? J'ai installé angular2-déplaçable et peut bien sûr utiliser la fonctionnalité sur tous les autres éléments.
mais parce que les boîtes de dialogue sont créées dynamiquement, Je ne peux pas utiliser ngdrageable sur un élément spécial ou peut utiliser une variable de modèle.
2 réponses
Puisqu'il n'y a pas de solution officielle pour cela, je vais écrire la directive personnalisée qui sera appliquée sur un titre de dialogue et faire tout le travail pour nous:
boîte de dialogue.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 {
L'idée de base ici est d'utiliser MatDialogRef.updatePosition
méthode pour mettre à jour la position de dialogue. Sous le capot cette méthode change les valeurs marge-haut|marge-gauche et quelqu'un peut faire valoir que ce n'est pas la meilleure option ici et ce serait mieux si nous utilisions transform mais je veux simplement montrer un exemple de comment nous pouvons le faire sans quelques astuces et avec l'aide des services intégrés.
nous devons également injecter MatDialogContainer dans notre directive afin que nous puissions obtenir la position initiale du conteneur de dialogue. Nous devons calculer l'offset initial. parce que la bibliothèque de matériel angulaire utilise la boîte de dialogue flex to center et elle ne nous donne pas de valeurs spécifiques de haut/gauche.
"151980920 boîte de dialogue"-déplaçable-titre.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;
}
se Souvenir
depuis que @ Rolando a demandé:
je veux "me rappeler" où le modal a été positionné de sorte que lorsque le bouton pour ouvrir le modal est touchée, la modale s'ouvre", il était la dernière situé'.
essayons de la soutenir.
pour ce faire, vous pouvez créer un service où vous stockerez les positions de dialogue:
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);
}
}
maintenant vous devez injecter ce service dans notre directive:
"151980920 boîte de dialogue"-déplaçable-titre.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 vous pouvez dès que le dialogue va être fermé, j'ai enregistrer le dernier décalage.
de cette façon, dialog se souvient de l'endroit où il a été fermé
dans angular2-draggable
, vous utilisez ngDraggable
pour rendre l'élément malléable.
où ngDraggable
est une directive et dans votre situation vous devez joindre cette directive ngDraggable
dynamiquement avec votre dialogue qui est créé dynamiquement.
bien qu'officiellement, il n'y ait aucun moyen d'ajouter une directive de manière dynamique, quelques astuces sales ont été discutées dans les questions suivantes pour ajouter une directive de manière dynamique.
comment dynamiquement ajouter une directive?
utiliser la Directive Angular2 dans l'hôte d'une autre Directive