Browse Source

Angular | Refactor modal component

pull/19858/head
Sinan997 2 years ago
parent
commit
268758665e
  1. 12
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html
  2. 149
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

12
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html

@ -1,9 +1,9 @@
<ng-content></ng-content> <ng-content></ng-content>
<ng-template #modalContent let-modal> <ng-template #modalContent let-modal>
@if (abpHeader) { @if (abpHeader()) {
<div id="abp-modal-header" class="modal-header abp-modal-header"> <div id="abp-modal-header" class="modal-header abp-modal-header">
<ng-container *ngTemplateOutlet="abpHeader"></ng-container> <ng-container *ngTemplateOutlet="abpHeader()"></ng-container>
​ ​
<button <button
id="abp-modal-close-button" id="abp-modal-close-button"
@ -14,14 +14,14 @@
></button> ></button>
</div> </div>
} }
@if (abpBody) { @if (abpBody()) {
<div id="abp-modal-body" class="modal-body"> <div id="abp-modal-body" class="modal-body">
<ng-container *ngTemplateOutlet="abpBody"></ng-container> <ng-container *ngTemplateOutlet="abpBody()"></ng-container>
</div> </div>
} }
@if (abpFooter) { @if (abpFooter()) {
<div id="abp-modal-footer" class="modal-footer"> <div id="abp-modal-footer" class="modal-footer">
<ng-container *ngTemplateOutlet="abpFooter"></ng-container> <ng-container *ngTemplateOutlet="abpFooter()"></ng-container>
</div> </div>
} }
</ng-template> </ng-template>

149
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

@ -1,20 +1,22 @@
import { SubscriptionService, uuid } from '@abp/ng.core'; import { SubscriptionService, uuid } from '@abp/ng.core';
import { import {
Component, Component,
ContentChild, DestroyRef,
EventEmitter,
Inject,
Input,
OnDestroy, OnDestroy,
OnInit, OnInit,
Optional,
Output,
TemplateRef, TemplateRef,
ViewChild, contentChild,
effect,
inject,
input,
model,
output,
viewChild,
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { fromEvent, Subject } from 'rxjs'; import { fromEvent } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators'; import { debounceTime, filter } from 'rxjs/operators';
import { Confirmation } from '../../models/confirmation'; import { Confirmation } from '../../models/confirmation';
import { ConfirmationService } from '../../services/confirmation.service'; import { ConfirmationService } from '../../services/confirmation.service';
import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token'; import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token';
@ -30,66 +32,51 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
providers: [SubscriptionService], providers: [SubscriptionService],
}) })
export class ModalComponent implements OnInit, OnDestroy, DismissableModal { export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
@Input() private confirmationService = inject(ConfirmationService);
get visible(): boolean { private modal = inject(NgbModal);
return this._visible; private modalRefService = inject(ModalRefService);
} private suppressUnsavedChangesWarningToken = inject(SUPPRESS_UNSAVED_CHANGES_WARNING, {
set visible(value: boolean) { optional: true,
if (typeof value !== 'boolean') return; });
this.toggle$.next(value); private destroyRef = inject(DestroyRef);
}
@Input()
get busy(): boolean {
return this._busy;
}
set busy(value: boolean) {
if (this.abpSubmit && this.abpSubmit instanceof ButtonComponent) {
this.abpSubmit.loading = value;
}
this._busy = value;
}
@Input() options: NgbModalOptions = {
keyboard: true,
};
@Input() suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; visible = model<boolean>(false);
@ViewChild('modalContent') modalContent?: TemplateRef<any>; busy = input(false, {
transform: (value: boolean) => {
if (this.abpSubmit() && this.abpSubmit() instanceof ButtonComponent) {
this.abpSubmit().loading = value;
}
return value;
},
});
@ContentChild('abpHeader', { static: false }) abpHeader?: TemplateRef<any>; options = input<NgbModalOptions>({ keyboard: true });
@ContentChild('abpBody', { static: false }) abpBody?: TemplateRef<any>; suppressUnsavedChangesWarning = input(this.suppressUnsavedChangesWarningToken);
@ContentChild('abpFooter', { static: false }) abpFooter?: TemplateRef<any>; modalContent = viewChild<TemplateRef<any>>('modalContent');
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpHeader = contentChild<TemplateRef<any>>('abpHeader');
abpSubmit?: ButtonComponent;
@Output() readonly visibleChange = new EventEmitter<boolean>(); abpBody = contentChild<TemplateRef<any>>('abpBody');
@Output() readonly init = new EventEmitter<void>(); abpFooter = contentChild<TemplateRef<any>>('abpFooter');
@Output() readonly appear = new EventEmitter<void>(); abpSubmit = contentChild(ButtonComponent, { read: ButtonComponent });
@Output() readonly disappear = new EventEmitter<void>(); readonly init = output();
_visible = false; readonly appear = output();
_busy = false; readonly disappear = output();
modalRef!: NgbModalRef; modalRef!: NgbModalRef;
isConfirmationOpen = false; isConfirmationOpen = false;
destroy$ = new Subject<void>();
modalIdentifier = `modal-${uuid()}`; modalIdentifier = `modal-${uuid()}`;
private toggle$ = new Subject<boolean>();
get modalWindowRef() { get modalWindowRef() {
return document.querySelector(`ngb-modal-window.${this.modalIdentifier}`); return document.querySelector(`ngb-modal-window.${this.modalIdentifier}`);
} }
@ -98,17 +85,12 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
return Boolean(this.modalWindowRef?.querySelector('.ng-dirty')); return Boolean(this.modalWindowRef?.querySelector('.ng-dirty'));
} }
constructor( constructor() {
private confirmationService: ConfirmationService, effect(() => {
private subscription: SubscriptionService, this.toggle(this.visible());
@Optional() });
@Inject(SUPPRESS_UNSAVED_CHANGES_WARNING)
private suppressUnsavedChangesWarningToken: boolean,
private modal: NgbModal,
private modalRefService: ModalRefService,
) {
this.initToggleStream();
} }
ngOnInit(): void { ngOnInit(): void {
this.modalRefService.register(this); this.modalRefService.register(this);
} }
@ -116,7 +98,7 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
dismiss(mode: ModalDismissMode) { dismiss(mode: ModalDismissMode) {
switch (mode) { switch (mode) {
case 'hard': case 'hard':
this.visible = false; this.visible.set(false);
break; break;
case 'soft': case 'soft':
this.close(); this.close();
@ -126,37 +108,29 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
} }
} }
private initToggleStream() {
this.subscription.addOne(this.toggle$.pipe(debounceTime(0), distinctUntilChanged()), value =>
this.toggle(value),
);
}
private toggle(value: boolean) { private toggle(value: boolean) {
this._visible = value; this.visible.set(value);
this.visibleChange.emit(value);
if (!value) { if (!value) {
this.modalRef?.dismiss(); this.modalRef?.dismiss();
this.disappear.emit(); this.disappear.emit();
this.destroy$.next();
return; return;
} }
setTimeout(() => this.listen(), 0); setTimeout(() => this.listen(), 0);
this.modalRef = this.modal.open(this.modalContent, { this.modalRef = this.modal.open(this.modalContent(), {
size: 'md', size: 'md',
centered: false, centered: false,
keyboard: false, keyboard: false,
scrollable: true, scrollable: true,
beforeDismiss: () => { beforeDismiss: () => {
if (!this.visible) return true; if (!this.visible()) return true;
this.close(); this.close();
return !this.visible; return !this.visible();
}, },
...this.options, ...this.options(),
windowClass: `${this.options.windowClass || ''} ${this.modalIdentifier}`, windowClass: `${this.options().windowClass || ''} ${this.modalIdentifier}`,
}); });
this.appear.emit(); this.appear.emit();
@ -165,30 +139,27 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
ngOnDestroy(): void { ngOnDestroy(): void {
this.modalRefService.unregister(this); this.modalRefService.unregister(this);
this.toggle(false); this.toggle(false);
this.destroy$.next();
} }
close() { close() {
if (this.busy) return; if (this.busy()) return;
if (this.isFormDirty && !this.suppressUnsavedChangesWarning) { if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) {
if (this.isConfirmationOpen) return; if (this.isConfirmationOpen) return;
this.isConfirmationOpen = true; this.isConfirmationOpen = true;
this.confirmationService this.confirmationService
.warn( .warn('AbpUi::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpUi::AreYouSure', {
'AbpUi::AreYouSureYouWantToCancelEditingWarningMessage', dismissible: false,
'AbpUi::AreYouSure', })
{ dismissible: false },
)
.subscribe((status: Confirmation.Status) => { .subscribe((status: Confirmation.Status) => {
this.isConfirmationOpen = false; this.isConfirmationOpen = false;
if (status === Confirmation.Status.confirm) { if (status === Confirmation.Status.confirm) {
this.visible = false; this.visible.set(false);
} }
}); });
} else { } else {
this.visible = false; this.visible.set(false);
} }
} }
@ -196,17 +167,17 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
if (this.modalWindowRef) { if (this.modalWindowRef) {
fromEvent<KeyboardEvent>(this.modalWindowRef, 'keyup') fromEvent<KeyboardEvent>(this.modalWindowRef, 'keyup')
.pipe( .pipe(
takeUntil(this.destroy$), takeUntilDestroyed(this.destroyRef),
debounceTime(150), debounceTime(150),
filter((key: KeyboardEvent) => key && key.key === 'Escape' && this.options.keyboard), filter((key: KeyboardEvent) => key && key.key === 'Escape' && this.options().keyboard),
) )
.subscribe(() => this.close()); .subscribe(() => this.close());
} }
fromEvent(window, 'beforeunload') fromEvent(window, 'beforeunload')
.pipe(takeUntil(this.destroy$)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => { .subscribe(event => {
if (this.isFormDirty && !this.suppressUnsavedChangesWarning) { if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) {
event.preventDefault(); event.preventDefault();
} }
}); });

Loading…
Cancel
Save