9

I have a vuejs component that displays a modal dialog with a small form inside. When the form is submitted I would like to hide the Modal but cannot figure out how to do it. When submitted the form calls a method in the parent.

Here is the component code

<template>
  <div>
    <div id="todoModal" class="modal fade" role="dialog">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h4 class="modal-title">{{ title }}</h4>
            <button type="button" class="close" data-dismiss="modal">
              &times;
            </button>
          </div>
          <div class="modal-body">
            <form id="todoForm" @submit.prevent="$emit('save')">
              <div class="form-group">
                <label for="name">Todo name</label>
                <input
                  id="name"
                  v-model="todo.name"
                  type="text"
                  class="form-control"
                  aria-describedby="nameHelp"
                  placeholder="Enter Todo Name"
                />
                <small id="nameHelp" class="form-text text-muted"
                  >Enter your todo's name</small
                >
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button class="btn btn-primary mr-4" type="submit" form="todoForm">
              <span v-if="mode == 'create'">Create</span>
              <span v-if="mode == 'update'">Update</span>
            </button>
            <button
              type="button"
              class="btn btn-danger mr-auto"
              data-dismiss="modal"
              @click="
                $parent.showModal = false;
                $parent.getTodos();
              "
            >
              Cancel
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "CreateTodo",
  props: ["mode", "title", "todo", "showModal"],
  ref: "createTodoModal",
  mounted() {
    if (this.mode == "create") {
      this.title = "Create Todo";
    }
  },
  methods: {}
};
</script>
<style scoped></style>

And here is my APP.js file

<template>
  <div id="app" class="container mt-5">
    <router-view
      ref="createtodo"
      class="CreateTodo"
      name="a"
      :todo="todo"
      :title="title"
      :mode="mode"
      :show-modal="showModal"
      @save="saveTodo"
    ></router-view>
  </div>
</template>

<script>
import { APIService } from "./APIServices";
const apiService = new APIService();

export default {
  name: "App",
  data: function() {
    return {
      mode: "create",
      title: "Create Todo",
      todo: {},
      todos: [],
      numberOfTodos: 0,
      showModal: false
    };
  },
  mounted: function() {
    this.getTodos();
  },

  methods: {
    saveTodo: function() {
      if (this.mode == "create") {
        apiService.createTodo(this.todo).then(
          result => {
            if (result.status === 200) {
              this.todo = result.data;
              this.getTodos();
            }
          },
          error => {}
        );
        this.showModal = false;
        // this.$refs.createtodo.$refs.todoModal
      } else if (this.mode == "update") {
        apiService.updateTodo(this.todo).then(
          result => {
            this.getTodos();
          },
          error => {}
        );
        this.showModal = false;
      } else if (this.mode == "update") {
        apiService.updateTodo(this.todo).then(
          result => {
            this.showModal = false;
            this.getTodos();
          },
          error => {}
        );
      }
    },
  }
};
</script>

<style lang="scss">
</style>

I tried using the ref to reference the Modal from APP.js but it does not work.

2
  • Review your property "showModal" It is not used to show/hide something. without bootstrap v-show directive could help. Commented May 17, 2019 at 0:23
  • It works, but the modal's dark background does not go away when the modal hides. Commented May 17, 2019 at 1:00

8 Answers 8

15

Add an id to the close X button"

<button type="button" data-dismiss="modal" aria-label="Close" id="close">
...
</button>

Then create a method to close the modal:

closeModal() {
   document.getElementById('close').click();
},
Sign up to request clarification or add additional context in comments.

Comments

14

Like @Dan Stoian reply, you can use ref in vue.js :

<button ref="Close" type="button" data-dismiss="modal" ...>
   ...
</button>

And in your handler

this.$refs.Close.click();

1 Comment

This is an elegant solution for Vue 3 / Boostrap 5. This completely suited a situation I had where an action taken from a modal (intentionally) removed the parent from a v-for iterator.
7

If you are using boostrap, you need to call the hide an show methods from it, because modal api create html elements on the fly (as the dark background)

I recommend to create a save method instead of call the $emit, where you can call the modal hide method before emit the save signal.

<script>
import $ from 'jquery'

export default {
  name: "CreateTodo",
  props: ["mode", "title", "todo"],
  ref: "createTodoModal",
  mounted() {
    if (this.mode == "create") {
      this.title = "Create Todo";
    }
  },
  methods: {
    save() {
       $('#ModalId').modal('hide')
       this.$emit('save')
    }
  }
};
</script>

showModal is not needed in this case.

Comments

3

Child

<template>
  <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby=""
    aria-hidden="true" ref="modalEle">
    <div class="modal-dialog modal-dialog-centered">
      <div class="modal-content">
        <div class="modal-header bg-primary text-white">
          <h5 class="modal-title" id="exampleModalLabel">{{ title }}</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <slot name="body" />
        </div>
        <div class="modal-footer">
          <slot name="footer"></slot>
          <button ref="Close" type="button" class="btn btn-secondary" data-bs-dismiss="modal">
            Close
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref, defineProps, defineExpose } from "vue";
import { Modal } from "bootstrap";

defineProps({
  title: {
    type: String,
    default: "<<Title goes here>>",
  },
});

let modalEle = ref(null);
let thisModalObj = null;

onMounted(() => {
  thisModalObj = new Modal(modalEle.value);
});

function _show() {
  thisModalObj.show();
}

function _close(){
  thisModalObj.hide()
}

defineExpose({ show: _show, close: _close });
</script>

Parent

<template>
  <Modal title="OMG ITS A MODAL" ref="thisModal">
    <template #body>
      <div class="col-md-12 mx-auto">
        yay modal text
      </div>
    </template>
    <template #footer>
      <!-- extra footer stuff !-->
    </template>
  </Modal>
</template>

<script setup>
import { ref } from 'vue';
import Modal from "../components/ModalCard.vue";

let showModal = ()=>{
  thisModal.value.show();
}

let closeModal = ()=>{
  thisModal.value.close();
}

</script>

Explanation

So basically what we are doing is exposing a child function to the parent via refs. In the _show and _close functions we are triggering the .show() and .hide() they are bootstrap modal functions we have access too via the thisModalObj. I hope this helps!

Now you can progmatically call thisModal.value.show and thisModal.value.close and itll show and close the modal.

1 Comment

awesome explanation on how to do this!
2

You might use v-if to show/hide modal

In your component:

  <div v-if="showModal">
    <div id="todoModal" class="modal fade" role="dialog">
    ...
    </div>
  </div>

and set showModal to true/false to show/hide component respectively.

3 Comments

Hi Ittus.. Thanks for the suggestion. I tried it and it does work, however when the modal hides the modal's background does not go away,
If you are using Bootstrap, you need to hide/show with boostrap methods.
1

you can use this npm package

npm i vue-js-modal

https://www.npmjs.com/package/vue-js-modal

Your code should then be modified in this way:

<template>
<modal :name="'edit-modal'+id" height="auto">
    <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Edit {{ mName }}</h5>
        <button type="button" class="close" @click="hideEditModal(id)">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    <form action="/user/update" method="patch" @submit.prevent="updateAssistant">
        <div class="modal-body">
            <div class="position-relative form-group">
                <label for="exampleAddress" class="">Full name</label><input name="name" v-model="editName" id="exampleAddress" placeholder="FullName" type="text" class="form-control">
                <div v-if="errors.has('editName')" class="alert alert-danger">
                    {{ errors.first('editName') }}
                </div>
            </div>

            <div class="position-relative form-group">
                <label for="exampleEmail11" class="">Email</label><input name="email" v-model="editEmail" id="exampleEmail11" placeholder="email" type="email" class="form-control">
                <div v-if="errors.has('editEmail')" class="alert alert-danger">
                    {{ errors.first('editEmail') }}
                </div>
            </div>

            <div class="position-relative form-group">
                <label for="examplePassword11" class="">Change Password</label><input name="password" v-model="editPassword" id="examplePassword11" placeholder="password" type="password" class="form-control">
                <div v-if="errors.has('password')" class="alert alert-danger">
                    {{ errors.first('password') }}
                </div>
            </div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" @click="hideEditModal(id)">Close</button>
            <button type="submit" class="btn btn-primary">Save changes</button>
        </div>
        <span class="loader" v-if="this.loading" style="position: absolute; bottom: 0.515rem; left: 20px;">
            <span class="ball-clip-rotate">
                <div style=" border:1px solid #000;"></div>
            </span>
        </span>
    </form>
</modal>

import Errors from '../../Errors.js'
export default {

name: 'AssistantEditModalComponent',

props: [
    'mEmail',
    'mName',
    'id'
],

data () {
    return {
        editPassword: null,
        disabled: false,
        errors: Errors,
        loading: false
    }
},

methods: {
    hideEditModal(id) {
        this.$modal.hide('edit-modal'+id);
    },

    setData() {
        this.editName = this.mName
        this.editEmail = this.mEmail
    },

    updateAssistant() {
        this.disabled = true;
        this.loading = true;
        const form = {
            editName: this.mName,
            editEmail: this.mEmail,
            password: this.editPassword
        }
        axios.patch('/user/update/'+this.id, form)
        .then((response) => {
            this.disabled = false
            this.loading = false
            this.hideEditModal(this.id)
            this.alert(response)
        })
        .catch((err) => {
            this.disabled = false
            this.loading = false
            Errors.fill(err.response.data.errors)
        })
    },

    alert(response) {
        swal(response.data.username, response.data.message, 'success')
    },
},

computed: {
    editName: {
        get: function() {
            return this.mName
        },
        set: function(value) {
            this.$emit('update:mName', value);
        }
    },
    editEmail: {
        get: function() {
            return this.mEmail
        },
        set: function(value) {
            this.$emit('update:mEmail', value);
        }
    }
}}

Comments

0

If you don't want to add any extra Close button rather than default X button of modal header you could do something like this:

<b-modal
    id="user-role"
    ref="role-modal"
    hide-footer
  >
...
</b-modal>

then in your method:

hideModal() {
    this.$refs["role-modal"].$refs['close-button'].click()
}

Comments

0

For Vuejs 3 this code work perfectly:

<template>
<div class="modal fade" id="theModal">
...
</div>
</template>

<script>
import { Modal } from 'bootstrap'

export default {
  data: {
    return {
      theModal: null,
    }
  }
  mounted() {
    this.theModal = new Modal('#theModal');
  },
  methods: {
    hideModal() {
      this.theModal.hide();
    }
  }
}
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.