Ionic Modal In Vue JS, Managing Events
Tags: tutorial, ionic, vue, javascript
Published: 2020-03-21 23:07:56

>This post is kinda old, there is a full example on using IonModal Here [No TS Ionic Vue Example](https://github.com/aaronksaunders/ionic-vue-starter-no-ts) If you have issues let me know.

-

The [Ionic Components](https://ionicframework.com/docs/components) for[VueJS](https://vuejs.org/) 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](https://github.com/aaronksaunders). Recently I was asked about handling modals, callbacks. I am going to quickly cover all of those in this blog post.

##See Video
{% youtube nEkJWKSXcDM %}


## Setting Up The Parent Component `App` to Call Modal

```html
<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>
```
first we import the modal component
```javascript
import SimpleModal from "./components/SimpleModal.vue";
```
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.
 
```javascript
modalCloseHandler(_value) {
   console.log("modal-closed", _value);

   if (_value.success) {
      // only on success
      alert(JSON.stringify(_value.noteInfo, null, 2));
   }
}
```
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.
```javascript
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})
}
```
This is the complete `<script>` section of the `App` component
```javascript
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})
    }
  }
};
```
### 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
```html
<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>
```
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`
```javascript
export default {
  name: "SimpleModal",
  props: ["timeStamp"],
  methods: {}
}
```
We specify the data fields for the form we are working with in the data section of the modal component.
```javascript
  data() {
    return {
      noteInfo: {
        title: "",
        description: ""
      }
    };
  }
```
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.
```javascript
methods: {
  modalClose: function(success) {
    let response = {
      success,
      noteInfo: success ? this.noteInfo : null
    };
    this.$ionic.modalController.dismiss(response);
  }
},
```
This is the complete `<script>` section of the `SimpleModal` component 
```javascript
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: ""
      }
    };
  }
};
```
## 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.
```javascript
/**
 * vue component lifecycle method where we setup listener
 * for when the modal is closed
 */
created() {
  this.$on("modal-closed", this.modalCloseHandler);
}
```
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`
```javascript
/**
 * 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();
}
```
We now handle the `dismiss` with the call inside of the `modalCloseHandler` 
```javascript
modalCloseHandler(_value) {
   console.log("modal-closed", _value);

   if (_value.success) {
      // only on success
      alert(JSON.stringify(_value.noteInfo, null, 2));
   }
},
```
### 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`
```javascript
modalClose: function(success) {
   let response = {
      success,
      noteInfo: success ? this.noteInfo : null
   };
   this.$parent.$emit("modal-closed", response);
}
```
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?
```html
<ion-item>
  <ion-input-vue
    type="text"
    name="title"
    placeholder="Title for note..."
    v-model="noteInfo.title"
    @ionChange="titleChanged"
  ></ion-input-vue>
</ion-item>
```
add the code for the function `titleChanged` to the `methods` section of the `SimpleModal` component
```javascript
titleChanged: function(_value) {
  this.$parent.$emit("modal-title-changed", _value.detail);
},
```
Then in the parent component `App` add an additional listener to the `onCreated` lifecycle event handler.
```javascript
  created() {
    this.$on("modal-closed", this.modalCloseHandler);
    this.$on("modal-title-changed", function(d) {
      console.log(d);
    });
  }
```
## Project Source Code
 Here is the gist with the source code from the project [Part One](https://gist.github.com/aaronksaunders/7292b87ee30aabee5d5bd5799fad0d5d)