'Bootstrap Modal Hide from VUE method

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.



Solution 1:[1]

Add an id to the close X button"

<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="close">
   <span aria-hidden="true">&times;</span>
</button>

Then create a method to close the modal:

closeModal() {
   document.getElementById('close').click();
},

Solution 2:[2]

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();

Solution 3:[3]

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.

Solution 4:[4]

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);
        }
    }
}}

Solution 5:[5]

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()
}

Solution 6:[6]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dan Stoian
Solution 2 abdelgrib
Solution 3 ittus
Solution 4 Simas Joneliunas
Solution 5 alireza.2281
Solution 6 Lee Taylor