Skip to content

Commit d82c56a

Browse files
committed
feat: add modal component and service for dynamic modal handling in example 11
1 parent 3b2c87c commit d82c56a

File tree

14 files changed

+368
-11
lines changed

14 files changed

+368
-11
lines changed

src/app/examples/advanced/example11/example11.module.ts renamed to assets/examples/advanced/example11/example11.component.html.md

File renamed without changes.
File renamed without changes.
File renamed without changes.

src/app/examples/advanced/example11/modal.component.ts renamed to src/app/examples/advanced/example11/custom-model/modal.component.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component } from '@angular/core';
2-
import { ModalRef } from './custom-model/model.ref';
2+
import { ModalRef } from './model.ref';
33

44
@Component({
55
selector: 'app-confirm-modal',
@@ -14,18 +14,41 @@ import { ModalRef } from './custom-model/model.ref';
1414
`,
1515
styles: [`
1616
.backdrop {
17-
position: fixed; inset: 0;
17+
position: fixed;
18+
inset: 0;
1819
background: rgba(0,0,0,0.5);
20+
z-index: 1000;
1921
}
2022
.modal {
2123
position: fixed;
2224
top: 50%; left: 50%;
2325
transform: translate(-50%, -50%);
24-
background: white;
25-
padding: 1rem;
26-
border-radius: 8px;
27-
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
28-
z-index: 1000;
26+
background: #fff;
27+
padding: 2rem;
28+
border-radius: 10px;
29+
box-shadow: 0 4px 16px rgba(0,0,0,0.25);
30+
min-width: 320px;
31+
min-height: 120px;
32+
z-index: 1001;
33+
display: flex;
34+
flex-direction: column;
35+
align-items: center;
36+
gap: 1rem;
37+
}
38+
.modal h2 {
39+
margin-bottom: 0.5rem;
40+
font-size: 1.3rem;
41+
font-weight: 600;
42+
}
43+
.modal p {
44+
color: #555;
45+
font-size: 1rem;
46+
margin-bottom: 1rem;
47+
}
48+
.modal button {
49+
min-width: 100px;
50+
font-size: 1rem;
51+
margin: 0 0.5rem;
2952
}
3053
`]
3154
})

src/app/examples/advanced/example11/custom-model/modal.service.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ export class ModalService {
2121
}
2222

2323
open<T extends object>(component: Type<T>, inputs?: Partial<T>): ModalRef<T> {
24+
// Always (re)create the modal container if missing
25+
let existing = document.getElementById('app-modal-container');
26+
if (existing) {
27+
this.modalContainer = existing;
28+
} else {
29+
this.modalContainer = document.createElement('div');
30+
this.modalContainer.id = 'app-modal-container';
31+
document.body.appendChild(this.modalContainer);
32+
}
33+
2434
// 1. Create container
2535
const containerRef = createComponent(ModalContainerComponent, {
2636
environmentInjector: this.appRef.injector as EnvironmentInjector,
@@ -38,11 +48,18 @@ export class ModalService {
3848

3949
// 3. Build ModalRef for inner component
4050
const modalRef = new ModalRef(cmpRef);
41-
42-
// optional: allow modal to close itself
4351
(cmpRef.instance as any).modalRef = modalRef;
4452

45-
// return reference to **inner component**
53+
// Remove modal and container from DOM on close/confirm
54+
const removeModal = () => {
55+
this.appRef.detachView(containerRef.hostView);
56+
if (this.modalContainer.parentNode) {
57+
this.modalContainer.parentNode.removeChild(this.modalContainer);
58+
}
59+
};
60+
modalRef.onClose.subscribe(removeModal);
61+
modalRef.onConfirm.subscribe(removeModal);
62+
4663
return modalRef;
4764
}
4865
}

src/app/examples/advanced/example11/custom-model/model-container.component.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,31 @@ export class ModalContainerComponent implements OnDestroy {
4040
Object.assign(this.childRef.instance, inputs);
4141
}
4242
(this.childRef.instance as any).modalRef = this.modalRef;
43+
44+
// Listen for close and confirm events to destroy modal
45+
if (this.modalRef) {
46+
this.modalRef.onClose.subscribe(() => {
47+
this.removeModal();
48+
});
49+
this.modalRef.onConfirm.subscribe(() => {
50+
this.removeModal();
51+
});
52+
}
4353
}
4454

4555
onBackdropClick() {
4656
this.modalRef.close();
4757
}
4858

59+
removeModal() {
60+
this.childRef?.destroy();
61+
// Remove modal container from DOM
62+
const el = (this.vc.element.nativeElement as HTMLElement).parentElement?.parentElement;
63+
if (el && el.parentElement) {
64+
el.parentElement.removeChild(el);
65+
}
66+
}
67+
4968
ngOnDestroy() {
5069
this.childRef?.destroy();
5170
}

src/app/examples/advanced/example11/example11.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Component, inject } from '@angular/core';
22
import { ModalService } from './custom-model/modal.service';
3-
import { ConfirmModalComponent } from './modal.component';
3+
import { ConfirmModalComponent } from './custom-model/modal.component';
44
import { MatTabsModule } from '@angular/material/tabs';
55
import { MatButtonModule } from '@angular/material/button';
66
import { RouterModule } from '@angular/router';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```html
2+
<div class="demo-section">
3+
<button mat-flat-button color="primary" (click)="openModal()">Open Modal</button>
4+
<ng-template #modalContainer></ng-template>
5+
</div>
6+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
```scss
2+
/* src/app/examples/advanced/example11/example11.component.scss */
3+
:host {
4+
display: contents;
5+
6+
.modal-backdrop {
7+
position: fixed;
8+
top: 0; left: 0; right: 0; bottom: 0;
9+
background: rgba(0,0,0,0.5);
10+
display: flex;
11+
align-items: center;
12+
justify-content: center;
13+
z-index: 1000;
14+
}
15+
.modal-content {
16+
background: #fff;
17+
padding: 2rem;
18+
border-radius: 8px;
19+
min-width: 300px;
20+
min-height: 100px;
21+
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
22+
}
23+
}
24+
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
```typescript
2+
import { Component, inject } from '@angular/core';
3+
import { ModalService } from './custom-model/modal.service';
4+
import { ConfirmModalComponent } from './custom-model/modal.component';
5+
6+
@Component({
7+
selector: 'app-advanced-example11',
8+
standalone: true,
9+
imports: [],
10+
templateUrl: './example11.component.html',
11+
styleUrls: ['./example11.component.scss']
12+
})
13+
export class Example11Component {
14+
private modalService = inject(ModalService);
15+
16+
openModal() {
17+
const modalRef = this.modalService.open(ConfirmModalComponent, {
18+
title: 'Delete Item?',
19+
message: 'Are you sure you want to delete this record?'
20+
});
21+
22+
modalRef.onClose.subscribe(() => {
23+
console.log('Modal closed');
24+
});
25+
26+
modalRef.onConfirm.subscribe(() => {
27+
console.log('Confirmed action');
28+
});
29+
30+
}
31+
}
32+
```

0 commit comments

Comments
 (0)