Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(modal): modifying dismiss behavior in child components #3117

Merged
merged 5 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/api/modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ import CanDismissPreventSwipeToCloseExample from '@site/static/usage/v7/modal/ca

<CanDismissPreventSwipeToCloseExample />

### Modifying dismiss behavior in child components

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.

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.

Here's a simplified example illustrating how a child component can interact with a parent component to modify the `canDismiss` callback:

import CanDismissChildStateExample from '@site/static/usage/v7/modal/can-dismiss/child-state/index.md';

<CanDismissChildStateExample />

## Types of modals

### Card Modal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
```ts
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';

import { IonicModule } from '@ionic/angular';

import { AppComponent } from './app.component';
import { ExampleComponent } from './example.component';
import { ChildComponent } from './child.component';

@NgModule({
imports: [BrowserModule, FormsModule, RouterModule.forRoot([]), IonicModule.forRoot({})],
declarations: [AppComponent, ExampleComponent, ChildComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
```html
<div class="ion-page">
<ion-header>
<ion-toolbar>
<ion-title>Modal</ion-title>
<ion-buttons slot="end">
<ion-button (click)="modal.dismiss()">Close</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-checkbox (ionChange)="checkboxChanged($event)">
Override Dismiss<br />
<ion-note class="ion-text-wrap"
>Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
>
</ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
</div>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
```ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

import { CheckboxCustomEvent, IonModal } from '@ionic/angular';

@Component({
selector: 'app-child',
templateUrl: 'child.component.html',
})
export class ChildComponent {
@Input() modal!: IonModal;

@Output() dismissChange = new EventEmitter<boolean>();

checkboxChanged(event: any) {
const ev = event as CheckboxCustomEvent;
const checked = ev.detail.checked;

this.dismissChange.emit(checked);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```html
<div class="ion-page">
<ion-header>
<ion-toolbar>
<ion-title>App</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-modal" expand="block">Open</ion-button>
<ion-modal
#modal
trigger="open-modal"
[canDismiss]="canDismiss"
[presentingElement]="presentingElement"
(willPresent)="onWillPresent()"
>
<ng-template>
<app-child [modal]="modal" (dismissChange)="onDismissChange($event)"></app-child>
</ng-template>
</ion-modal>
</ion-content>
</div>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
```ts
import { Component } from '@angular/core';

import { ActionSheetController } from '@ionic/angular';

@Component({
selector: 'app-example',
templateUrl: 'example.component.html',
})
export class ExampleComponent {
presentingElement = undefined;

private canDismissOverride = false;

constructor(private actionSheetCtrl: ActionSheetController) {}

ngOnInit() {
this.presentingElement = document.querySelector('.ion-page');
}

onDismissChange(canDismiss: boolean) {
// Allows the modal to be dismissed based on the state of the checkbox
this.canDismissOverride = canDismiss;
}

onWillPresent() {
// Resets the override when the modal is presented
this.canDismissOverride = false;
}

canDismiss = async () => {
if (this.canDismissOverride) {
// Checks for the override flag to return early if we can dismiss the overlay immediately
return true;
}

const actionSheet = await this.actionSheetCtrl.create({
header: 'Are you sure?',
buttons: [
{
text: 'Yes',
role: 'confirm',
},
{
text: 'No',
role: 'cancel',
},
],
});

actionSheet.present();

const { role } = await actionSheet.onWillDismiss();

return role === 'confirm';
};
}
```
92 changes: 92 additions & 0 deletions static/usage/v7/modal/can-dismiss/child-state/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Modal | Can Dismiss</title>
<link rel="stylesheet" href="../../../../common.css" />
<script src="../../../../common.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" />
</head>

<body>
<ion-app>
<div class="ion-page">
<ion-header>
<ion-toolbar>
<ion-title>App</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="open-modal" expand="block">Open</ion-button>

<ion-modal trigger="open-modal">
<ion-header>
<ion-toolbar>
<ion-title>Modal</ion-title>
<ion-buttons slot="end">
<ion-button onclick="dismiss()">Close</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-checkbox id="override-dismiss">
Override Dismiss<br />
<ion-note class="ion-text-wrap"
>Toggle the checkbox to allow immediately dismissing the modal without a prompt.</ion-note
>
</ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
</ion-modal>
</ion-content>
</div>
</ion-app>

<script>
const modal = document.querySelector('ion-modal');

modal.canDismiss = promptClose;
modal.presentingElement = document.querySelector('.ion-page');

let canDismissCheckbox = false;

function dismiss() {
modal.dismiss();
}
const checkbox = document.querySelector('#override-dismiss');
checkbox.addEventListener('ionChange', (event) => {
canDismissCheckbox = event.detail.checked;
});

async function promptClose() {
if (canDismissCheckbox) {
return true;
}
const actionSheet = document.createElement('ion-action-sheet');

actionSheet.header = 'Are you sure?';
actionSheet.buttons = [
{
text: 'Yes',
role: 'confirm',
},
{
text: 'No',
role: 'cancel',
},
];
document.body.appendChild(actionSheet);
await actionSheet.present();

const { role } = await actionSheet.onWillDismiss();

return role === 'confirm';
}
</script>
</body>
</html>
42 changes: 42 additions & 0 deletions static/usage/v7/modal/can-dismiss/child-state/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Playground from '@site/src/components/global/Playground';

import vue_example_vue from './vue/example_vue.md';
import vue_child_vue from './vue/child_vue.md';

import react_main_tsx from './react/main_tsx.md';
import react_child_tsx from './react/child_tsx.md';

import angular_example_component_html from './angular/example_component_html.md';
import angular_example_component_ts from './angular/example_component_ts.md';
import angular_child_component_html from './angular/child_component_html.md';
import angular_child_component_ts from './angular/child_component_ts.md';
import angular_app_module_ts from './angular/app_module_ts.md';

<Playground
version="7"
code={{
react: {
files: {
'src/main.tsx': react_main_tsx,
'src/Child.tsx': react_child_tsx,
},
},
vue: {
files: {
'src/components/Example.vue': vue_example_vue,
'src/components/Child.vue': vue_child_vue,
},
},
angular: {
files: {
'src/app/example.component.html': angular_example_component_html,
'src/app/example.component.ts': angular_example_component_ts,
'src/app/child.component.html': angular_child_component_html,
'src/app/child.component.ts': angular_child_component_ts,
'src/app/app.module.ts': angular_app_module_ts,
},
},
}}
src="usage/v7/modal/can-dismiss/child-state/demo.html"
devicePreview
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
```tsx
import {
IonHeader,
IonToolbar,
IonTitle,
IonButtons,
IonButton,
IonContent,
IonList,
IonItem,
IonCheckbox,
IonNote,
} from '@ionic/react';

import type { CheckboxCustomEvent } from '@ionic/core/components';

interface ChildProps {
dismissChange: (checked: boolean) => void;
dismiss: () => void;
}

function Child({ dismissChange, dismiss }: ChildProps) {
const checkboxChanged = (ev: CheckboxCustomEvent) => {
const checked = ev.detail.checked;
dismissChange(checked);
};

return (
<>
<IonHeader>
<IonToolbar>
<IonTitle>Modal</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => dismiss()}>Close</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
<IonList>
<IonItem>
<IonCheckbox onIonChange={checkboxChanged}>
Override Dismiss
<br />
<IonNote className="ion-text-wrap">
Toggle the checkbox to allow immediately dismissing the modal without a prompt.
</IonNote>
</IonCheckbox>
</IonItem>
</IonList>
</IonContent>
</>
);
}
export default Child;
```
Loading
Loading