Skip to content

Commit be7e132

Browse files
authored
docs(modal): modifying dismiss behavior in child components (#3117)
1 parent 2b9c065 commit be7e132

File tree

12 files changed

+580
-0
lines changed

12 files changed

+580
-0
lines changed

docs/api/modal.md

+12
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ import CanDismissPreventSwipeToCloseExample from '@site/static/usage/v7/modal/ca
8787

8888
<CanDismissPreventSwipeToCloseExample />
8989

90+
### Modifying dismiss behavior in child components
91+
92+
In certain scenarios, developers may need to customize the behavior of the `canDismiss` callback based on the state of a presented modal. This customization can be particularly useful, for instance, when developers want to prevent the modal from being dismissed if a form within it is invalid.
93+
94+
To achieve this customization, child components can employ various techniques such as function callbacks, event emission, or other reactivity mechanisms to communicate with the parent component and update the conditions governing the `canDismiss` callback.
95+
96+
Here's a simplified example illustrating how a child component can interact with a parent component to modify the `canDismiss` callback:
97+
98+
import CanDismissChildStateExample from '@site/static/usage/v7/modal/can-dismiss/child-state/index.md';
99+
100+
<CanDismissChildStateExample />
101+
90102
## Types of modals
91103

92104
### Card Modal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
```ts
2+
import { NgModule } from '@angular/core';
3+
import { FormsModule } from '@angular/forms';
4+
import { BrowserModule } from '@angular/platform-browser';
5+
import { RouterModule } from '@angular/router';
6+
7+
import { IonicModule } from '@ionic/angular';
8+
9+
import { AppComponent } from './app.component';
10+
import { ExampleComponent } from './example.component';
11+
import { ChildComponent } from './child.component';
12+
13+
@NgModule({
14+
imports: [BrowserModule, FormsModule, RouterModule.forRoot([]), IonicModule.forRoot({})],
15+
declarations: [AppComponent, ExampleComponent, ChildComponent],
16+
bootstrap: [AppComponent],
17+
})
18+
export class AppModule {}
19+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
```html
2+
<div class="ion-page">
3+
<ion-header>
4+
<ion-toolbar>
5+
<ion-title>Modal</ion-title>
6+
<ion-buttons slot="end">
7+
<ion-button (click)="modal.dismiss()">Close</ion-button>
8+
</ion-buttons>
9+
</ion-toolbar>
10+
</ion-header>
11+
<ion-content>
12+
<ion-list>
13+
<ion-item>
14+
<ion-checkbox (ionChange)="checkboxChanged($event)">
15+
Override Dismiss<br />
16+
<ion-note class="ion-text-wrap"
17+
>Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
18+
>
19+
</ion-checkbox>
20+
</ion-item>
21+
</ion-list>
22+
</ion-content>
23+
</div>
24+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
```ts
2+
import { Component, Input, Output, EventEmitter } from '@angular/core';
3+
4+
import { CheckboxCustomEvent, IonModal } from '@ionic/angular';
5+
6+
@Component({
7+
selector: 'app-child',
8+
templateUrl: 'child.component.html',
9+
})
10+
export class ChildComponent {
11+
@Input() modal!: IonModal;
12+
13+
@Output() dismissChange = new EventEmitter<boolean>();
14+
15+
checkboxChanged(event: any) {
16+
const ev = event as CheckboxCustomEvent;
17+
const checked = ev.detail.checked;
18+
19+
this.dismissChange.emit(checked);
20+
}
21+
}
22+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
```html
2+
<div class="ion-page">
3+
<ion-header>
4+
<ion-toolbar>
5+
<ion-title>App</ion-title>
6+
</ion-toolbar>
7+
</ion-header>
8+
<ion-content class="ion-padding">
9+
<ion-button id="open-modal" expand="block">Open</ion-button>
10+
<ion-modal
11+
#modal
12+
trigger="open-modal"
13+
[canDismiss]="canDismiss"
14+
[presentingElement]="presentingElement"
15+
(willPresent)="onWillPresent()"
16+
>
17+
<ng-template>
18+
<app-child [modal]="modal" (dismissChange)="onDismissChange($event)"></app-child>
19+
</ng-template>
20+
</ion-modal>
21+
</ion-content>
22+
</div>
23+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
```ts
2+
import { Component } from '@angular/core';
3+
4+
import { ActionSheetController } from '@ionic/angular';
5+
6+
@Component({
7+
selector: 'app-example',
8+
templateUrl: 'example.component.html',
9+
})
10+
export class ExampleComponent {
11+
presentingElement = undefined;
12+
13+
private canDismissOverride = false;
14+
15+
constructor(private actionSheetCtrl: ActionSheetController) {}
16+
17+
ngOnInit() {
18+
this.presentingElement = document.querySelector('.ion-page');
19+
}
20+
21+
onDismissChange(canDismiss: boolean) {
22+
// Allows the modal to be dismissed based on the state of the checkbox
23+
this.canDismissOverride = canDismiss;
24+
}
25+
26+
onWillPresent() {
27+
// Resets the override when the modal is presented
28+
this.canDismissOverride = false;
29+
}
30+
31+
canDismiss = async () => {
32+
if (this.canDismissOverride) {
33+
// Checks for the override flag to return early if we can dismiss the overlay immediately
34+
return true;
35+
}
36+
37+
const actionSheet = await this.actionSheetCtrl.create({
38+
header: 'Are you sure?',
39+
buttons: [
40+
{
41+
text: 'Yes',
42+
role: 'confirm',
43+
},
44+
{
45+
text: 'No',
46+
role: 'cancel',
47+
},
48+
],
49+
});
50+
51+
actionSheet.present();
52+
53+
const { role } = await actionSheet.onWillDismiss();
54+
55+
return role === 'confirm';
56+
};
57+
}
58+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Modal | Can Dismiss</title>
7+
<link rel="stylesheet" href="../../../../common.css" />
8+
<script src="../../../../common.js"></script>
9+
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script>
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" />
11+
</head>
12+
13+
<body>
14+
<ion-app>
15+
<div class="ion-page">
16+
<ion-header>
17+
<ion-toolbar>
18+
<ion-title>App</ion-title>
19+
</ion-toolbar>
20+
</ion-header>
21+
<ion-content class="ion-padding">
22+
<ion-button id="open-modal" expand="block">Open</ion-button>
23+
24+
<ion-modal trigger="open-modal">
25+
<ion-header>
26+
<ion-toolbar>
27+
<ion-title>Modal</ion-title>
28+
<ion-buttons slot="end">
29+
<ion-button onclick="dismiss()">Close</ion-button>
30+
</ion-buttons>
31+
</ion-toolbar>
32+
</ion-header>
33+
<ion-content>
34+
<ion-list>
35+
<ion-item>
36+
<ion-checkbox id="override-dismiss">
37+
Override Dismiss<br />
38+
<ion-note class="ion-text-wrap"
39+
>Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
40+
>
41+
</ion-checkbox>
42+
</ion-item>
43+
</ion-list>
44+
</ion-content>
45+
</ion-modal>
46+
</ion-content>
47+
</div>
48+
</ion-app>
49+
50+
<script>
51+
const modal = document.querySelector('ion-modal');
52+
53+
modal.canDismiss = promptClose;
54+
modal.presentingElement = document.querySelector('.ion-page');
55+
56+
let canDismissCheckbox = false;
57+
58+
function dismiss() {
59+
modal.dismiss();
60+
}
61+
const checkbox = document.querySelector('#override-dismiss');
62+
checkbox.addEventListener('ionChange', (event) => {
63+
canDismissCheckbox = event.detail.checked;
64+
});
65+
66+
async function promptClose() {
67+
if (canDismissCheckbox) {
68+
return true;
69+
}
70+
const actionSheet = document.createElement('ion-action-sheet');
71+
72+
actionSheet.header = 'Are you sure?';
73+
actionSheet.buttons = [
74+
{
75+
text: 'Yes',
76+
role: 'confirm',
77+
},
78+
{
79+
text: 'No',
80+
role: 'cancel',
81+
},
82+
];
83+
document.body.appendChild(actionSheet);
84+
await actionSheet.present();
85+
86+
const { role } = await actionSheet.onWillDismiss();
87+
88+
return role === 'confirm';
89+
}
90+
</script>
91+
</body>
92+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Playground from '@site/src/components/global/Playground';
2+
3+
import vue_example_vue from './vue/example_vue.md';
4+
import vue_child_vue from './vue/child_vue.md';
5+
6+
import react_main_tsx from './react/main_tsx.md';
7+
import react_child_tsx from './react/child_tsx.md';
8+
9+
import angular_example_component_html from './angular/example_component_html.md';
10+
import angular_example_component_ts from './angular/example_component_ts.md';
11+
import angular_child_component_html from './angular/child_component_html.md';
12+
import angular_child_component_ts from './angular/child_component_ts.md';
13+
import angular_app_module_ts from './angular/app_module_ts.md';
14+
15+
<Playground
16+
version="7"
17+
code={{
18+
react: {
19+
files: {
20+
'src/main.tsx': react_main_tsx,
21+
'src/Child.tsx': react_child_tsx,
22+
},
23+
},
24+
vue: {
25+
files: {
26+
'src/components/Example.vue': vue_example_vue,
27+
'src/components/Child.vue': vue_child_vue,
28+
},
29+
},
30+
angular: {
31+
files: {
32+
'src/app/example.component.html': angular_example_component_html,
33+
'src/app/example.component.ts': angular_example_component_ts,
34+
'src/app/child.component.html': angular_child_component_html,
35+
'src/app/child.component.ts': angular_child_component_ts,
36+
'src/app/app.module.ts': angular_app_module_ts,
37+
},
38+
},
39+
}}
40+
src="usage/v7/modal/can-dismiss/child-state/demo.html"
41+
devicePreview
42+
/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
```tsx
2+
import {
3+
IonHeader,
4+
IonToolbar,
5+
IonTitle,
6+
IonButtons,
7+
IonButton,
8+
IonContent,
9+
IonList,
10+
IonItem,
11+
IonCheckbox,
12+
IonNote,
13+
} from '@ionic/react';
14+
15+
import type { CheckboxCustomEvent } from '@ionic/core/components';
16+
17+
interface ChildProps {
18+
dismissChange: (checked: boolean) => void;
19+
dismiss: () => void;
20+
}
21+
22+
function Child({ dismissChange, dismiss }: ChildProps) {
23+
const checkboxChanged = (ev: CheckboxCustomEvent) => {
24+
const checked = ev.detail.checked;
25+
dismissChange(checked);
26+
};
27+
28+
return (
29+
<>
30+
<IonHeader>
31+
<IonToolbar>
32+
<IonTitle>Modal</IonTitle>
33+
<IonButtons slot="end">
34+
<IonButton onClick={() => dismiss()}>Close</IonButton>
35+
</IonButtons>
36+
</IonToolbar>
37+
</IonHeader>
38+
<IonContent>
39+
<IonList>
40+
<IonItem>
41+
<IonCheckbox onIonChange={checkboxChanged}>
42+
Override Dismiss
43+
<br />
44+
<IonNote className="ion-text-wrap">
45+
Toggle the checkbox to allow immediately dismissing the modal without a prompt.
46+
</IonNote>
47+
</IonCheckbox>
48+
</IonItem>
49+
</IonList>
50+
</IonContent>
51+
</>
52+
);
53+
}
54+
export default Child;
55+
```

0 commit comments

Comments
 (0)