web-dev-qa-db-fra.com

Comment puis-je créer un matériau MatDialog déplaçable/angulaire

Est-il possible de rendre un dialogue de matériau angulaire déplaçable? J'ai installé angular2-draggable et peux bien sûr utiliser la fonctionnalité sur tous les autres éléments.

Mais comme les dialogues sont créés dynamiquement, je ne peux pas utiliser ngDraggable sur un élément spécial ni utiliser une variable de modèle.

16
HansDampfHH

Mise à jour depuis le matériau angulaire 7

Vous pouvez simplement utiliser cdkDrag directive from @angular/cdk/drag-drop

dialog.html

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

Exemple Stackblitz

Réponse précédente:

Puisqu'il n'y a pas de solution officielle pour cela, je vais écrire une directive personnalisée qui sera appliquée à un titre de dialogue et fera tout le travail à notre place:

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 {

Exemple Ng-run

 enter image description here

L'idée de base est d'utiliser la méthode MatDialogRef.updatePosition pour mettre à jour la position du dialogue. Sous le capot, cette méthode modifie les valeurs margin-top | margin-left et quelqu'un peut dire que ce n'est pas la meilleure option et qu'il serait préférable d'utiliser la transformation, mais je veux simplement montrer un exemple de la façon dont nous pouvons le faire sans astuces et avec l'aide des services intégrés.

Nous devons également injecter MatDialogContainer dans notre directive afin d’obtenir la position initiale du conteneur de dialogue. Nous devons calculer le décalage initial, car la bibliothèque de matériaux angulaires utilise le dialogue flex au centre et ne nous donne pas de valeurs haut/gauche spécifiques.

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

Rappelez-vous l'emplacement

Depuis que @Rolando a demandé:

Je veux «me rappeler» où le modal était positionné de telle sorte que lorsque le bouton pour ouvrir le modal est frappé, le modal s'ouvre là où il était "dernier situé'.

essayons de le supporter.

Pour ce faire, vous pouvez créer un service dans lequel vous enregistrerez des 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:

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

Comme vous pouvez dès que le dialogue va être fermé, je sauvegarde le dernier décalage.

Exemple Ng-run

Le dialogue de cette façon se souvient où il a été fermé

 enter image description here

25
yurzui

dans angular2-draggable, vous utilisez ngDraggable pour rendre l'élément glissable . où ngDraggable est une directive et, dans votre cas, vous devez attacher cette directive ngDraggable de manière dynamique à votre boîte de dialogue créée de manière dynamique.

Bien qu’officiellement, il n’existe aucun moyen d’ajouter une directive de manière dynamique, mais certaines astuces ont été abordées dans les questions suivantes pour l’ajouter de manière dynamique.

Comment ajouter de manière dynamique une directive?

Utiliser la directive Angular2 dans l'hôte d'une autre directive

0
WasiF