-
+
+
+
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
index 2527ab033d..f587e8fe78 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
@@ -1,3 +1,4 @@
+import { takeUntilDestroy } from '@abp/ng.core';
import {
Component,
ContentChild,
@@ -12,10 +13,11 @@ import {
ViewChildren,
} from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
-import { debounceTime, filter, takeUntil } from 'rxjs/operators';
+import { debounceTime, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators';
import { fadeAnimation } from '../../animations/modal.animations';
import { Confirmation } from '../../models/confirmation';
import { ConfirmationService } from '../../services/confirmation.service';
+import { ModalService } from '../../services/modal.service';
import { ButtonComponent } from '../button/button.component';
export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
@@ -33,20 +35,7 @@ export class ModalComponent implements OnDestroy {
}
set visible(value: boolean) {
if (typeof value !== 'boolean') return;
-
- this.isModalOpen = value;
- this._visible = value;
- this.visibleChange.emit(value);
-
- if (value) {
- setTimeout(() => this.listen(), 0);
- this.renderer.addClass(document.body, 'modal-open');
- this.appear.emit();
- } else {
- this.renderer.removeClass(document.body, 'modal-open');
- this.disappear.emit();
- this.destroy$.next();
- }
+ this.toggle$.next(value);
}
@Input()
@@ -79,6 +68,8 @@ export class ModalComponent implements OnDestroy {
@ContentChild('abpClose', { static: false, read: ElementRef })
abpClose: ElementRef
;
+ @ViewChild('template', { static: false }) template: TemplateRef;
+
@ViewChild('abpModalContent', { static: false }) modalContent: ElementRef;
@ViewChildren('abp-button') abpButtons;
@@ -101,11 +92,43 @@ export class ModalComponent implements OnDestroy {
destroy$ = new Subject();
+ private toggle$ = new Subject();
+
get isFormDirty(): boolean {
return Boolean(document.querySelector('.modal-dialog .ng-dirty'));
}
- constructor(private renderer: Renderer2, private confirmationService: ConfirmationService) {}
+ constructor(
+ private renderer: Renderer2,
+ private confirmationService: ConfirmationService,
+ private modalService: ModalService,
+ ) {
+ this.initToggleStream();
+ }
+
+ private initToggleStream() {
+ this.toggle$
+ .pipe(takeUntilDestroy(this), debounceTime(0), distinctUntilChanged())
+ .subscribe(value => this.toggle(value));
+ }
+
+ private toggle(value: boolean) {
+ this.isModalOpen = value;
+ this._visible = value;
+ this.visibleChange.emit(value);
+
+ if (value) {
+ this.modalService.renderTemplate(this.template);
+ setTimeout(() => this.listen(), 0);
+ this.renderer.addClass(document.body, 'modal-open');
+ this.appear.emit();
+ } else {
+ this.modalService.clearModal();
+ this.renderer.removeClass(document.body, 'modal-open');
+ this.disappear.emit();
+ this.destroy$.next();
+ }
+ }
ngOnDestroy(): void {
this.destroy$.next();
@@ -141,9 +164,7 @@ export class ModalComponent implements OnDestroy {
debounceTime(150),
filter((key: KeyboardEvent) => key && key.key === 'Escape'),
)
- .subscribe(() => {
- this.close();
- });
+ .subscribe(() => this.close());
fromEvent(window, 'beforeunload')
.pipe(takeUntil(this.destroy$))
@@ -151,6 +172,7 @@ export class ModalComponent implements OnDestroy {
if (this.isFormDirty) {
event.returnValue = true;
} else {
+ event.returnValue = false;
delete event.returnValue;
}
});
diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts
index 3bdf2e9d07..00562c4780 100644
--- a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts
+++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts
@@ -1,9 +1,13 @@
import { LocalizationPipe } from '@abp/ng.core';
+import { RouterTestingModule } from '@angular/router/testing';
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
-import { timer } from 'rxjs';
+import { fromEvent, Subject, timer } from 'rxjs';
+import { delay, reduce, take } from 'rxjs/operators';
import { ButtonComponent, ConfirmationComponent, ModalComponent } from '../components';
-import { RouterTestingModule } from '@angular/router/testing';
+import { ModalContainerComponent } from '../components/modal/modal-container.component';
+import { Confirmation } from '../models';
+import { ConfirmationService, ModalService } from '../services';
describe('ModalComponent', () => {
let spectator: SpectatorHost<
@@ -12,16 +16,34 @@ describe('ModalComponent', () => {
>;
let appearFn;
let disappearFn;
+ let mockConfirmation$: Subject;
const createHost = createHostFactory({
component: ModalComponent,
imports: [RouterTestingModule],
- declarations: [ConfirmationComponent, LocalizationPipe, ButtonComponent],
+ declarations: [
+ ConfirmationComponent,
+ LocalizationPipe,
+ ButtonComponent,
+ ModalContainerComponent,
+ ],
+ entryComponents: [ModalContainerComponent],
+ providers: [
+ {
+ provide: ConfirmationService,
+ useValue: {
+ warn() {
+ mockConfirmation$ = new Subject();
+ return mockConfirmation$;
+ },
+ },
+ },
+ ],
mocks: [Store],
});
- beforeEach(() => {
- appearFn = jest.fn(() => null);
- disappearFn = jest.fn(() => null);
+ beforeEach(async () => {
+ appearFn = jest.fn();
+ disappearFn = jest.fn();
spectator = createHost(
`
@@ -30,102 +52,170 @@ describe('ModalComponent', () => {
-
-
-
+
-