Skip to content

Commit

Permalink
Angular | Refactor modal component
Browse files Browse the repository at this point in the history
  • Loading branch information
Sinan997 committed May 18, 2024
1 parent 7f07c0c commit 2687586
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<ng-content></ng-content>

<ng-template #modalContent let-modal>
@if (abpHeader) {
@if (abpHeader()) {
<div id="abp-modal-header" class="modal-header abp-modal-header">
<ng-container *ngTemplateOutlet="abpHeader"></ng-container>
<ng-container *ngTemplateOutlet="abpHeader()"></ng-container>
<button
id="abp-modal-close-button"
Expand All @@ -14,14 +14,14 @@
></button>
</div>
}
@if (abpBody) {
@if (abpBody()) {
<div id="abp-modal-body" class="modal-body">
<ng-container *ngTemplateOutlet="abpBody"></ng-container>
<ng-container *ngTemplateOutlet="abpBody()"></ng-container>
</div>
}
@if (abpFooter) {
@if (abpFooter()) {
<div id="abp-modal-footer" class="modal-footer">
<ng-container *ngTemplateOutlet="abpFooter"></ng-container>
<ng-container *ngTemplateOutlet="abpFooter()"></ng-container>
</div>
}
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { SubscriptionService, uuid } from '@abp/ng.core';
import {
Component,
ContentChild,
EventEmitter,
Inject,
Input,
DestroyRef,
OnDestroy,
OnInit,
Optional,
Output,
TemplateRef,
ViewChild,
contentChild,
effect,
inject,
input,
model,
output,
viewChild,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators';
import { fromEvent } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
import { Confirmation } from '../../models/confirmation';
import { ConfirmationService } from '../../services/confirmation.service';
import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token';
Expand All @@ -30,66 +32,51 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
providers: [SubscriptionService],
})
export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
@Input()
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
if (typeof value !== 'boolean') return;
this.toggle$.next(value);
}

@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,
};
private confirmationService = inject(ConfirmationService);
private modal = inject(NgbModal);
private modalRefService = inject(ModalRefService);
private suppressUnsavedChangesWarningToken = inject(SUPPRESS_UNSAVED_CHANGES_WARNING, {
optional: true,
});
private destroyRef = inject(DestroyRef);

@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 })
abpSubmit?: ButtonComponent;
abpHeader = contentChild<TemplateRef<any>>('abpHeader');

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

isConfirmationOpen = false;

destroy$ = new Subject<void>();

modalIdentifier = `modal-${uuid()}`;

private toggle$ = new Subject<boolean>();

get modalWindowRef() {
return document.querySelector(`ngb-modal-window.${this.modalIdentifier}`);
}
Expand All @@ -98,25 +85,20 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
return Boolean(this.modalWindowRef?.querySelector('.ng-dirty'));
}

constructor(
private confirmationService: ConfirmationService,
private subscription: SubscriptionService,
@Optional()
@Inject(SUPPRESS_UNSAVED_CHANGES_WARNING)
private suppressUnsavedChangesWarningToken: boolean,
private modal: NgbModal,
private modalRefService: ModalRefService,
) {
this.initToggleStream();
constructor() {
effect(() => {
this.toggle(this.visible());
});
}

ngOnInit(): void {
this.modalRefService.register(this);
}

dismiss(mode: ModalDismissMode) {
switch (mode) {
case 'hard':
this.visible = false;
this.visible.set(false);
break;
case 'soft':
this.close();
Expand All @@ -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) {
this._visible = value;
this.visibleChange.emit(value);
this.visible.set(value);

if (!value) {
this.modalRef?.dismiss();
this.disappear.emit();
this.destroy$.next();
return;
}

setTimeout(() => this.listen(), 0);
this.modalRef = this.modal.open(this.modalContent, {
this.modalRef = this.modal.open(this.modalContent(), {
size: 'md',
centered: false,
keyboard: false,
scrollable: true,
beforeDismiss: () => {
if (!this.visible) return true;
if (!this.visible()) return true;

this.close();
return !this.visible;
return !this.visible();
},
...this.options,
windowClass: `${this.options.windowClass || ''} ${this.modalIdentifier}`,
...this.options(),
windowClass: `${this.options().windowClass || ''} ${this.modalIdentifier}`,
});

this.appear.emit();
Expand All @@ -165,48 +139,45 @@ export class ModalComponent implements OnInit, OnDestroy, DismissableModal {
ngOnDestroy(): void {
this.modalRefService.unregister(this);
this.toggle(false);
this.destroy$.next();
}

close() {
if (this.busy) return;
if (this.busy()) return;

if (this.isFormDirty && !this.suppressUnsavedChangesWarning) {
if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) {
if (this.isConfirmationOpen) return;

this.isConfirmationOpen = true;
this.confirmationService
.warn(
'AbpUi::AreYouSureYouWantToCancelEditingWarningMessage',
'AbpUi::AreYouSure',
{ dismissible: false },
)
.warn('AbpUi::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpUi::AreYouSure', {
dismissible: false,
})
.subscribe((status: Confirmation.Status) => {
this.isConfirmationOpen = false;
if (status === Confirmation.Status.confirm) {
this.visible = false;
this.visible.set(false);
}
});
} else {
this.visible = false;
this.visible.set(false);
}
}

listen() {
if (this.modalWindowRef) {
fromEvent<KeyboardEvent>(this.modalWindowRef, 'keyup')
.pipe(
takeUntil(this.destroy$),
takeUntilDestroyed(this.destroyRef),
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());
}

fromEvent(window, 'beforeunload')
.pipe(takeUntil(this.destroy$))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => {
if (this.isFormDirty && !this.suppressUnsavedChangesWarning) {
if (this.isFormDirty && !this.suppressUnsavedChangesWarning()) {
event.preventDefault();
}
});
Expand Down

0 comments on commit 2687586

Please sign in to comment.