I have some difficulties with figuring out how to make my modals draggable. I have reusable modals with its own service which is called to create one inside components.
confirm.modal.service.ts
import { Injectable } from "@angular/core"; import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; import { Observable, from, EMPTY, throwError } from "rxjs"; import { catchError, tap } from "rxjs/operators"; import { ConfirmModalComponent } from "./confirm-modal.component"; export interface ConfirmOptions { title: string; subtitle?: string; errorOnClose?: boolean; } @Injectable({ providedIn: "root" }) export class ConfirmModalService { constructor(private modalService: NgbModal) {} confirm(options: ConfirmOptions): Observable<boolean> { const modalRef = this.modalService.open(ConfirmModalComponent, { centered: true }); modalRef.componentInstance.title = options.title || "Are you sure?"; modalRef.componentInstance.subtitle = options.subtitle || null; return from(modalRef.result).pipe( tap(), catchError(err => options.errorOnClose ? throwError(err || "not confirmed") : EMPTY ) ); } }
confirm.modal.module.ts
import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { DragDropModule } from "@angular/cdk/drag-drop"; import { ConfirmModalComponent } from "./confirm-modal.component"; @NgModule({ imports: [ CommonModule, DragDropModule ], declarations: [ConfirmModalComponent], exports: [ConfirmModalComponent] }) export class ConfirmModalModule {}
confirm.modal.component.ts
import { Component, Input } from "@angular/core"; import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap"; @Component({ selector: "app-confirm-modal", templateUrl: "./confirm-modal.component.html", styleUrls: ["./confirm-modal.component.scss"] }) export class ConfirmModalComponent { @Input() title: string; @Input() subtitle: string; constructor(public activeModal: NgbActiveModal) {} public accept(): void { this.activeModal.close(true); } public dismiss(): void { this.activeModal.close(false); } }
confirm.modal.component.html
<div class="modal-body"> <div class="modal-body__header"> <span>{{ title }}</span> </div> <div *ngIf="subtitle" class="modal-body__text"> <span>{{ subtitle }}</span> </div> <div class="modal-body__button-row"> <button class="btn btn-primary" (click)="accept()">Yes</button> <button class="btn btn-light" (click)="dismiss()">Cancel</button> </div> </div>
So I want to make the whole modal be draggable with Angular built-in DragDropModule, hence I should add cdkDrag
inside element with class='modal-content'
but I don’t how to achieve that with current setup. NgbModalOptions provides functionality to add class only but not attribute directive.
I know that there is easier solution with JQuery draggable, but I would like to avoid that.
I was thinking about using @ViewChildren for each page but it doesn’t seem to the best solution for me.
Thanks for any help!
Advertisement
Answer
Just wrap your modal inside a container and add the cdkDragRootElement to it as per the documentation. You will also have to add this class as an option when you open the dialog from the component.ts.
<ng-template #content let-modal> <div cdkDrag cdkDragRootElement=".your-custom-dialog-class"> <div class="modal-header"> </div> <div class="modal-body"> </div> <div class="modal-footer"> </div> </div> </ng-template>
The code for the component.ts
const options: NgbModalOptions = { windowClass: 'your-custom-dialog-class' }; this.modalService.open(this.content, options);