DEV Community

Cover image for Ionic Modal In Vue JS, Managing Events
Aaron K Saunders
Aaron K Saunders

Posted on • Edited on

Ionic Modal In Vue JS, Managing Events

This post is kinda old, there is a full example on using IonModal Here No TS Ionic Vue Example If you have issues let me know.

-

The Ionic Components forVueJS are still in beta, but I have been using them for a while now and occasionally go back and update some of the samples I have posted in my github repo. Recently I was asked about handling modals, callbacks. I am going to quickly cover all of those in this blog post.

See Video

Setting Up The Parent Component App to Call Modal

<template>
  <ion-app>
    <ion-page>
      <ion-header>
        <ion-toolbar color="primary">
          <ion-title>Modal Test App</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content padding>
        <ion-button @click="openModal">Show Modal</ion-button>
      </ion-content>
    </ion-page>
  </ion-app>
</template>
Enter fullscreen mode Exit fullscreen mode

first we import the modal component

import SimpleModal from "./components/SimpleModal.vue";
Enter fullscreen mode Exit fullscreen mode

Inside the script tag for our page, in the methods, section we create a modalCloseHandler method that will be called when the modal is closed.

modalCloseHandler(_value) {
   console.log("modal-closed", _value);

   if (_value.success) {
      // only on success
      alert(JSON.stringify(_value.noteInfo, null, 2));
   }
}
Enter fullscreen mode Exit fullscreen mode

then we have the function openModal that will actually open the modal. In this example I am passing in a property timeStamp just as a way to show how properties are passed into the component when using Ionic.

We next call modal.present() to actually show the modal.

Then finally wait for a response with modal.onDidDismiss()

We need to handle the scenario where the user clicks the backdrop to exit the modal; in that situation we do not want to process the response.

async openModal() {
   let modal = await this.$ionic.modalController.create({
      component: SimpleModal,
         componentProps: {
            propsData: {
               timeStamp: new Date()
            }
         }
      });

   // show the modal
   await modal.present();

   // wait for a response when closing the modal
   let modalResponse = await modal.onDidDismiss();

   // when dismissed by backdrop click, data is undefined,
   // we only process a response from the user behavior
   modalResponse.data && this.modalCloseHandler({...modalResponse.data})
}
Enter fullscreen mode Exit fullscreen mode

This is the complete <script> section of the App component

import SimpleModal from "./components/SimpleModal.vue";

export default {
  name: "App",
  components: {},
  methods: {
    /**
     * called when the modal is closed
     */
    modalCloseHandler(_value) {
      console.log("modal-closed", _value);

      if (_value.success) {
        // only on success
        alert(JSON.stringify(_value.noteInfo, null, 2));
      }
    },
    /**
     * when the user clicks button, we open the modal
     */
    async openModal() {
      let modal = await this.$ionic.modalController.create({
        component: SimpleModal,
        componentProps: {
          parent: this,
          propsData: {
            timeStamp: new Date()
          }
        }
      });

      // show the modal
      await modal.present();

      // wait to see if i get a response
      let modalResponse = await modal.onDidDismiss();

      // when dismissed by clicking outside of modal,
      // data is undefined so we do not handle it
      modalResponse.data && this.modalCloseHandler({...modalResponse.data})
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

The Modal Component - SimpleModal

Please note that the input elements are specific to vue; We are using the vue specific input elements ion-input-vue and ion-textarea-vue

We handle the button click events by calling modalClose(true) when user wants to save the data and modalClose(false) when user clicks cancel

<template>
  <div>
    <ion-header>
      <ion-toolbar>
        <ion-title>Note Modal</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content padding>
      <ion-item>
        <ion-label color="primary" position="floating">Title</ion-label>
        <ion-input-vue
          type="text"
          name="title"
          placeholder="Title for note..."
          v-model="noteInfo.title"
        ></ion-input-vue>
      </ion-item>

      <ion-item>
        <ion-label color="primary" position="floating">Description</ion-label>
        <ion-textarea-vue rows="5" placeholder="Note description" v-model="noteInfo.description"></ion-textarea-vue>
      </ion-item>

      <ion-item style="font-size:smaller; text-align: center" lines="none">
        <ion-label>{{(timeStamp +"").split('(')[0]}}</ion-label>
      </ion-item>
      <ion-row>
        <ion-col>
          <ion-button expand="block" @click="modalClose(true)">Save Note</ion-button>
        </ion-col>
        <ion-col>
          <ion-button expand="block" color="danger" @click="modalClose(false)">Cancel</ion-button>
        </ion-col>
      </ion-row>
    </ion-content>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Inside the code/ script tag section of the component be sure to specify the properties that are being passed into the component; in this case it is just the timeStamp

export default {
  name: "SimpleModal",
  props: ["timeStamp"],
  methods: {}
}
Enter fullscreen mode Exit fullscreen mode

We specify the data fields for the form we are working with in the data section of the modal component.

  data() {
    return {
      noteInfo: {
        title: "",
        description: ""
      }
    };
  }
Enter fullscreen mode Exit fullscreen mode

And finally the modalClose function in the methods section. Here we return the data from the form if success is true otherwise we return null.

to pass the information back to the parent onDismiss listener, we access the controller this.$ionic.modalController and call the dismiss method passing the response data as the parameter.

methods: {
  modalClose: function(success) {
    let response = {
      success,
      noteInfo: success ? this.noteInfo : null
    };
    this.$ionic.modalController.dismiss(response);
  }
},
Enter fullscreen mode Exit fullscreen mode

This is the complete <script> section of the SimpleModal component

export default {
  name: "SimpleModal",
  props: ["timeStamp"],
  methods: {
    modalClose: function(success) {
      let response = {
        success,
        noteInfo: success ? this.noteInfo : null
      };
      this.$ionic.modalController.dismiss(response);
    }
  },
  data() {
    return {
      noteInfo: {
        title: "",
        description: ""
      }
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

Using Vue Event Emitters

Here we are building off of the previous section where we demonstrated how to use a modal form to present information in a vuejs application using Ionic Framework Components.

More information on Custom Events: https://vuejs.org/v2/guide/components-custom-events.html

In this example, we will show how to use standard vue $emit to get a similar result. This is also an approach for managing events from the Modal component other than actually closing the modal.

Setting Up App Component

The the App component lifecycle event created we add the following code. This will listen for the modal-closed event from the SimpleModal vue component.

/**
 * vue component lifecycle method where we setup listener
 * for when the modal is closed
 */
created() {
  this.$on("modal-closed", this.modalCloseHandler);
}
Enter fullscreen mode Exit fullscreen mode

Next we need to make a change to how we call the component to appropriately handle the event.

First we add the property parent to the component so we can send the event back to this component, the parent; we assign it the vale this

Also notice there is no more listening for onDismiss and process the response; all of that is now handled with the event listener modal-closed

/**
 * when the user clicks button, we open the modal
 */
async openModal() {
  let modal = await this.$ionic.modalController.create({
    component: SimpleModal,
    componentProps: {
      parent: this,
      propsData: {
        timeStamp: new Date()
      }
    }
  });

  // show the modal
  await modal.present();
}
Enter fullscreen mode Exit fullscreen mode

We now handle the dismiss with the call inside of the modalCloseHandler

modalCloseHandler(_value) {
   console.log("modal-closed", _value);

   if (_value.success) {
      // only on success
      alert(JSON.stringify(_value.noteInfo, null, 2));
   }
},
Enter fullscreen mode Exit fullscreen mode

Changes To SimpleModal Component

The only change needed here is to modify the modalClose method to emit the event instead of calling this.$ionic.modalController.dismiss

modalClose: function(success) {
   let response = {
      success,
      noteInfo: success ? this.noteInfo : null
   };
   this.$parent.$emit("modal-closed", response);
}
Enter fullscreen mode Exit fullscreen mode

Either approach can work, but I wanted to investigate an approach to process events from the Modal without having to actually close the modal and this approach can solve that problem.

Benefits of Emitting Events

We dont always want to just close the modal...Useless example, tracking when a form field changes?

<ion-item>
  <ion-input-vue
    type="text"
    name="title"
    placeholder="Title for note..."
    v-model="noteInfo.title"
    @ionChange="titleChanged"
  ></ion-input-vue>
</ion-item>
Enter fullscreen mode Exit fullscreen mode

add the code for the function titleChanged to the methods section of the SimpleModal component

titleChanged: function(_value) {
  this.$parent.$emit("modal-title-changed", _value.detail);
},
Enter fullscreen mode Exit fullscreen mode

Then in the parent component App add an additional listener to the onCreated lifecycle event handler.

  created() {
    this.$on("modal-closed", this.modalCloseHandler);
    this.$on("modal-title-changed", function(d) {
      console.log(d);
    });
  }
Enter fullscreen mode Exit fullscreen mode

Project Source Code

Here is the gist with the source code from the project Part One

Top comments (16)

Collapse
 
davids89 profile image
David

It is possible to bind props with a new modal instance?

Collapse
 
aaronksaunders profile image
Aaron K Saunders • Edited

not sure I understand the question?

Also, I don't create modals this way anymore, I use the template approach, I find it to be much cleaner

Take a look here: github.com/aaronksaunders/ionic-vu...

Collapse
 
suzannevangelder profile image
Suzanne-van-Gelder

I think David means data: { name: "Why Data" } in Detail.vue does not bind to the props in SimpleModal.vue. The default values are used in the Modal.

I have the same problem and get the warning:
[Vue warn]: Extraneous non-props attributes (data) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

I tried to figure it out myself, but am a little bit stuck by now.

Thread Thread
 
aaronksaunders profile image
Aaron K Saunders

have you looked at the updated code sample referenced above? This post is over a year old and ionic has changed how they manage modals. If you have a code sample project somewhere I am happy to take a look

Thread Thread
 
suzannevangelder profile image
Suzanne-van-Gelder

Yes, I have cloned the project with updated code from github.com/aaronksaunders/ionic-vu....

I have changed
data: {
name: "Why Data"
}
into
data: {
title: "Why Data"
}
but both do not show "Why Data" in the title of the modal, but "Super Modal" instead.

I run Ionic Framework : @ionic/vue 5.5.2 with vue@3.0.4

Thread Thread
 
aaronksaunders profile image
Aaron K Saunders

to change the title of the modal, you need to set the modals title property, you are changing the data property which has no impact on the modal

Thread Thread
 
suzannevangelder profile image
Suzanne-van-Gelder

I think you mean:
props: {
title: { type: String, default: "Super Modal" }
},
But how do I change this using parameters from detail.vue?

Thread Thread
 
aaronksaunders profile image
Aaron K Saunders
    <simple-modal
      :title="data"
      @modalSave="handleSave"
      @modalCancel="handleCancel">
    </simple-modal>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
suzannevangelder profile image
Suzanne-van-Gelder

Thank you, it's working now.

Thread Thread
 
aaronksaunders profile image
Aaron K Saunders

Thanks, please like and subscribe to my YouTube channel, trying to get my subscribers over 2000!! And you will get updated when I push new content

Collapse
 
ideasdelaboratorio profile image
ideasdelaboratorio

How can it be adapted to be able to call the modal from any other component?
Thanks

Collapse
 
aaronksaunders profile image
Aaron K Saunders

i dont this that is how component based frameworks are meant to work. You should put the contents of the modal in a separate component that can be utilized on different locations in your app

Collapse
 
ideasdelaboratorio profile image
ideasdelaboratorio

Thanks. I meant the modal component to avoid the import in every component it's called. The content of the modal differs if arrive from one button or link.
Vuejs Event bus can be used in ionic?

Thread Thread
 
aaronksaunders profile image
Aaron K Saunders

yes Ionic just provides web components, all the regular vue stuff is still there

Collapse
 
visibly profile image
visibly

Thanks for the example. Have you succeeded to add swipeToClose to modals?
I'm having issues getting this to work, but I don't know if it's even supported in the Ionic Vue beta?

For reference this is my method for opening the modal (which works perfectly fine - except for the swipeToClose):

openTaskDrawer(task) { return this.modal = this.$ionic.modalController .create({ component: TaskDrawer, presentingElement: this.$el, swipeToClose: true, componentProps: { propsData: { task: task } }, }) .then(m => m.present()) }

Collapse
 
aaronksaunders profile image
Aaron K Saunders

tryed to make a video, but jacked up the sound... here it is for now until i get a chance to redo the video track youtu.be/nEkJWKSXcDM