import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { ModalPage } from '../modal/modal.page';
@Component({
selector: 'modal-example',
templateUrl: 'modal-example.html',
styleUrls: ['./modal-example.css'],
})
export class ModalExample {
constructor(public modalController: ModalController) {}
async presentModal() {
const modal = await this.modalController.create({
component: ModalPage,
cssClass: 'my-custom-class',
});
return await modal.present();
}
}
import { Component, Input } from '@angular/core';
@Component({
selector: 'modal-page',
})
export class ModalPage {
constructor() {}
}
If you need a wrapper element inside of your modal component, we recommend using a <div class="ion-page">
so that the component dimensions are still computed properly.
During creation of a modal, data can be passed in through the componentProps
.
The previous example can be written to include data:
async presentModal() {
const modal = await this.modalController.create({
component: ModalPage,
cssClass: 'my-custom-class',
componentProps: {
'firstName': 'Douglas',
'lastName': 'Adams',
'middleInitial': 'N'
}
});
return await modal.present();
}
To get the data passed into the componentProps
, set it as an @Input
:
export class ModalPage {
@Input() firstName: string;
@Input() lastName: string;
@Input() middleInitial: string;
}
A modal can be dismissed by calling the dismiss method on the modal controller and optionally passing any data from the modal.
export class ModalPage {
...
dismiss() {
this.modalController.dismiss({
'dismissed': true
});
}
}
After being dismissed, the data can be read in through the onWillDismiss
or onDidDismiss
attached to the modal after creation:
const { data } = await modal.onWillDismiss();
console.log(data);
When lazy loading a modal, it's important to note that the modal will not be loaded when it is opened, but rather when the module that imports the modal's module is loaded.
For example, say there exists a CalendarComponent
and an EventModal
. The modal is presented by clicking a button in the CalendarComponent
. In Angular, the EventModalModule
would need to be included in the CalendarComponentModule
since the modal is created in the CalendarComponent
:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { CalendarComponent } from './calendar.component';
import { EventModalModule } from '../modals/event/event.module';
@NgModule({
declarations: [CalendarComponent],
imports: [IonicModule, CommonModule, EventModalModule],
exports: [CalendarComponent],
})
export class CalendarComponentModule {}
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the --backdrop-opacity
variable will not have any effect.
If you are creating an application that uses ion-tabs
, it is recommended that you get the parent ion-router-outlet
using this.routerOutlet.parentOutlet.nativeEl
, otherwise the tabbar will not scale down when the modal opens.
import { IonRouterOutlet } from '@ionic/angular';
constructor(private routerOutlet: IonRouterOutlet) {}
async presentModal() {
const modal = await this.modalController.create({
component: ModalPage,
cssClass: 'my-custom-class',
swipeToClose: true,
presentingElement: this.routerOutlet.nativeEl
});
return await modal.present();
}
In most scenarios, using the ion-router-outlet
element as the presentingElement
is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal
element as the presentingElement
.
import { ModalController } from '@ionic/angular';
constructor(private modalController: ModalController) {}
async presentModal() {
const modal = await this.modalController.create({
component: ModalPage,
cssClass: 'my-custom-class',
swipeToClose: true,
presentingElement: await this.modalController.getTop()
});
return await modal.present();
}
In Angular, the CSS of a specific page is scoped only to elements of that page. Even though the Modal can be presented from within a page, the ion-modal
element is appended outside of the current page. This means that any custom styles need to go in a global stylesheet file. In an Ionic Angular starter this can be the src/global.scss
file or you can register a new global style file by adding to the styles
build option in angular.json
.
customElements.define(
'modal-page',
class extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Modal Header</ion-title>
<ion-buttons slot="primary">
<ion-button onClick="dismissModal()">
<ion-icon slot="icon-only" name="close"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
Modal Content
</ion-content>`;
}
}
);
function presentModal() {
const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
document.body.appendChild(modalElement);
return modalElement.present();
}
If you need a wrapper element inside of your modal component, we recommend using a <div class="ion-page">
so that the component dimensions are still computed properly.
During creation of a modal, data can be passed in through the componentProps
. The previous example can be written to include data:
const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.componentProps = {
firstName: 'Douglas',
lastName: 'Adams',
middleInitial: 'N',
};
To get the data passed into the componentProps
, query for the modal in the modal-page
:
customElements.define('modal-page', class extends HTMLElement {
connectedCallback() {
const modalElement = document.querySelector('ion-modal');
console.log(modalElement.componentProps.firstName);
...
}
}
A modal can be dismissed by calling the dismiss method and optionally passing any data from the modal.
async function dismissModal() {
await modal.dismiss({
dismissed: true,
});
}
After being dismissed, the data can be read in through the onWillDismiss
or onDidDismiss
attached to the modal after creation:
const { data } = await modalElement.onWillDismiss();
console.log(data);
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the --backdrop-opacity
variable will not have any effect.
const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.swipeToClose = true;
modalElement.presentingElement = document.querySelector('ion-nav');
In most scenarios, using the ion-nav
element as the presentingElement
is fine. In cases where you are presenting a card-style modal from within a modal, you should pass in the top-most ion-modal
element as the presentingElement
.
const modalElement = document.createElement('ion-modal');
modalElement.component = 'modal-page';
modalElement.cssClass = 'my-custom-class';
modalElement.swipeToClose = true;
modalElement.presentingElement = await modalController.getTop();
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
const Body: React.FC<{
count: number;
onDismiss: () => void;
onIncrement: () => void;
}> = ({ count, onDismiss, onIncrement }) => (
<div>
count: {count}
<IonButton expand="block" onClick={() => onIncrement()}>
Increment Count
</IonButton>
<IonButton expand="block" onClick={() => onDismiss()}>
Close
</IonButton>
</div>
);
const ModalExample: React.FC = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleDismiss = () => {
dismiss();
};
const [present, dismiss] = useIonModal(Body, {
count,
onDismiss: handleDismiss,
onIncrement: handleIncrement,
});
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() => {
present({
cssClass: 'my-class',
});
}}
>
Show Modal
</IonButton>
<div>Count: {count}</div>
</IonContent>
</IonPage>
);
};
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';
export const ModalExample: React.FC = () => {
const [showModal, setShowModal] = useState(false);
return (
<IonContent>
<IonModal isOpen={showModal} cssClass="my-custom-class">
<p>This is modal content</p>
<IonButton onClick={() => setShowModal(false)}>Close Modal</IonButton>
</IonModal>
<IonButton onClick={() => setShowModal(true)}>Show Modal</IonButton>
</IonContent>
);
};
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the --backdrop-opacity
variable will not have any effect.
const App: React.FC = () => {
const routerRef = useRef<HTMLIonRouterOutletElement | null>(null);
return (
<IonApp>
<IonReactRouter>
<IonRouterOutlet ref={routerRef}>
<Route path="/home" render={() => <Home router={routerRef.current} />} exact={true} />
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
)
};
...
interface HomePageProps {
router: HTMLIonRouterOutletElement | null;
}
const Home: React.FC<HomePageProps> = ({ router }) => {
const [showModal, setShowModal] = useState(false);
return (
...
<IonModal
isOpen={showModal}
cssClass='my-custom-class'
swipeToClose={true}
presentingElement={router || undefined}
onDidDismiss={() => setShowModal(false)}>
<p>This is modal content</p>
</IonModal>
...
);
};
In most scenarios, setting a ref on IonRouterOutlet
and passing that ref's current
value to presentingElement
is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal
ref as the presentingElement
.
<IonModal
ref={firstModalRef}
isOpen={showModal}
cssClass='my-custom-class'
swipeToClose={true}
presentingElement={router || undefined}
onDidDismiss={() => setShowModal(false)}>
<p>This is modal content</p>
<IonButton onClick={() => setShow2ndModal(true)}>Show 2nd Modal</IonButton>
<IonButton onClick={() => setShowModal(false)}>Close Modal</IonButton>
</IonModal>
<IonModal
isOpen={show2ndModal}
cssClass='my-custom-class'
presentingElement={firstModalRef.current}
onDidDismiss={() => setShow2ndModal(false)}>
<p>This is more modal content</p>
<IonButton onClick={() => setShow2ndModal(false)}>Close Modal</IonButton>
</IonModal>
import { Component, h } from '@stencil/core';
import { modalController } from '@ionic/core';
@Component({
tag: 'modal-example',
styleUrl: 'modal-example.css',
})
export class ModalExample {
async presentModal() {
const modal = await modalController.create({
component: 'page-modal',
cssClass: 'my-custom-class',
});
await modal.present();
}
}
import { Component, h } from '@stencil/core';
@Component({
tag: 'page-modal',
styleUrl: 'page-modal.css',
})
export class PageModal {
render() {
return [
<ion-list>
<ion-item>
<ion-label>Documentation</ion-label>
</ion-item>
<ion-item>
<ion-label>Feedback</ion-label>
</ion-item>
<ion-item>
<ion-label>Settings</ion-label>
</ion-item>
</ion-list>,
];
}
}
If you need a wrapper element inside of your modal component, we recommend using a <div class="ion-page">
so that the component dimensions are still computed properly.
During creation of a modal, data can be passed in through the componentProps
.
The previous example can be written to include data:
async presentModal() {
const modal = await modalController.create({
component: 'page-modal',
cssClass: 'my-custom-class',
componentProps: {
'firstName': 'Douglas',
'lastName': 'Adams',
'middleInitial': 'N'
}
});
await modal.present();
}
To get the data passed into the componentProps
, set each one as a @Prop
:
import { Component, Prop, h } from '@stencil/core';
@Component({
tag: 'page-modal',
styleUrl: 'page-modal.css',
})
export class PageModal {
@Prop() firstName: string;
@Prop() lastName: string;
@Prop() middleInitial: string;
}
A modal can be dismissed by calling the dismiss method on the modal controller and optionally passing any data from the modal.
export class ModalPage {
...
dismiss(data?: any) {
(this.el.closest('ion-modal') as any).dismiss({
'dismissed': true
});
}
}
After being dismissed, the data can be read in through the onWillDismiss
or onDidDismiss
attached to the modal after creation:
const { data } = await modal.onWillDismiss();
console.log(data);
Modals in iOS mode have the ability to be presented in a card-style and swiped to close. The card-style presentation and swipe to close gesture are not mutually exclusive, meaning you can pick and choose which features you want to use. For example, you can have a card-style modal that cannot be swiped or a full sized modal that can be swiped.
Card style modals when running on iPhone-sized devices do not have backdrops. As a result, the --backdrop-opacity
variable will not have any effect.
import { Component, Element, h } from '@stencil/core';
import { modalController } from '@ionic/core';
@Component({
tag: 'modal-example',
styleUrl: 'modal-example.css',
})
export class ModalExample {
@Element() el: any;
async presentModal() {
const modal = await modalController.create({
component: 'page-modal',
cssClass: 'my-custom-class',
swipeToClose: true,
presentingElement: this.el.closest('ion-router-outlet'),
});
await modal.present();
}
}
In most scenarios, using the ion-router-outlet
element as the presentingElement
is fine. In cases where you are presenting a card-style modal from within another modal, you should pass in the top-most ion-modal
element as the presentingElement
.
async presentModal() {
const modal = await modalController.create({
component: 'page-modal',
cssClass: 'my-custom-class',
swipeToClose: true,
presentingElement: await modalController.getTop()
});
await modal.present();
}
<template>
<ion-header>
<ion-toolbar>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> {{ content }} </ion-content>
</template>
<script>
import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Modal',
props: {
title: { type: String, default: 'Super Modal' },
},
data() {
return {
content: 'Content',
};
},
components: { IonContent, IonHeader, IonTitle, IonToolbar },
});
</script>
<template>
<ion-page>
<ion-content class="ion-padding">
<ion-button @click="openModal">Open Modal</ion-button>
</ion-content>
</ion-page>
</template>
<script>
import { IonButton, IonContent, IonPage, modalController } from '@ionic/vue';
import Modal from './modal.vue';
export default {
components: { IonButton, IonContent, IonPage },
methods: {
async openModal() {
const modal = await modalController.create({
component: Modal,
cssClass: 'my-custom-class',
componentProps: {
title: 'New Title',
},
});
return modal.present();
},
},
};
</script>
Developers can also use this component directly in their template:
<template>
<ion-button @click="setOpen(true)">Show Modal</ion-button>
<ion-modal :is-open="isOpenRef" css-class="my-custom-class" @didDismiss="setOpen(false)">
<Modal :data="data"></Modal>
</ion-modal>
</template>
<script>
import { IonModal, IonButton } from '@ionic/vue';
import { defineComponent, ref } from 'vue';
import Modal from './modal.vue';
export default defineComponent({
components: { IonModal, IonButton, Modal },
setup() {
const isOpenRef = ref(false);
const setOpen = (state: boolean) => (isOpenRef.value = state);
const data = { content: 'New Content' };
return { isOpenRef, setOpen, data };
},
});
</script>
If you need a wrapper element inside of your modal component, we recommend using an <ion-page>
so that the component dimensions are still computed properly.