Is it feasible to trigger a modal to open from a source external to the Vue

How can a component be made reusable by calling a method from outside the component?

Currently, I include a button to open the modal in a template slot:

index.php

<modal>
    <template slot="button">
        <button class="btn">Open modal</button>
    </template>
    Some modal text
</modal>

Modal.vue

<template>
    <div>
        <div @click="showModal"><slot name="button"></slot></div>
        <div v-if="showingModal"><slot></slot></div>
    </div>
</template>

<script>
    export default {

        data () {
            return {
                showingModal: false,
            }
        },

        methods: {
            showModal() {
                this.showingModal = true;
            },
        }
    }
</script>

I believe there is a better solution available, but I have been unable to find it.

Answer №1

A method can be called from outside the component!

Parent component

<template>
 <div>
   <modal ref="modal"></modal>
   <button @click="openModal">Open Modal</button>
 </div>
</template>

<script>
  import modal from './child.vue'
  export default {
    components: { modal }
    methods: {
     openModal() { this.$refs.modal.show() }//calling the show method of child
    }
  }
</script>

Child component

<template>
  <div v-if="showModal">
    <div id="modal">
      <p>Hello, I am a modal
      </p>
      <button @click="hide">Close</button>
    </div> 
  </div>
</template>

<script>
 export default {
   data() {
     return {
      showModal: false
     }
   },
   methods: {
     show() {
      this.showModal = true
     },
     hide(){
      this.showModal = false
     }
   }
 }
</script>

Check it out in action here

Answer №2

Insert your modal component instance into the Vue.prototype, then trigger the show/hide methods wherever you have access to the Vue instance context.

Check out the demo below:

let vm = null // the instance for your Vue modal
let timeout = null //async/delay popup

const SModal = {
  isActive: false,

  show ({
    delay = 500,
    message = '',
    customClass = 'my-modal-class'
  } = {}) {
    if (this.isActive) {
      vm && vm.$forceUpdate()
      return
    }

    timeout = setTimeout(() => {
      timeout = null

      const node = document.createElement('div')
      document.body.appendChild(node)
      let staticClass = ''
      vm = new this.__Vue({
        name: 's-modal',
        el: node,
        render (h) { 
          return h('div', {
            staticClass,
            'class': customClass,
            domProps: {
              innerHTML: message
            }
          })
        }
      })
    }, delay)

    this.isActive = true
  },
  hide () {
    if (!this.isActive) {
      return
    }

    if (timeout) {
      clearTimeout(timeout)
      timeout = null
    } else {
      vm.$destroy()
      document.body.removeChild(vm.$el)
      vm = null
    }

    this.isActive = false
  },

  __Vue: null,
  __installed: false,
  install ({ $my, Vue }) {
    if (this.__installed) { return }
    this.__installed = true
    $my.SModal = SModal 
    this.__Vue = Vue 
  }
}

let installFunc = function (_Vue, opts = {}) {
  if (this.__installed) {
    return
  }
  this.__installed = true
  const $my = {
    'memo': 'I am a plugin management.'
  }
  if (opts.plugins) {
    Object.keys(opts.plugins).forEach(key => {
      const p = opts.plugins[key]
      if (typeof p.install === 'function') {
        p.install({ $my, Vue: _Vue })
      }
    }
  }
  _Vue.prototype.$my = $my
}

Vue.use(installFunc, {
  plugins: [SModal]
})

app = new Vue({
  el: "#app",
  data: {
    'test 1': 'Cat in Boots'
  },
  methods: {
    openModal: function () {
      this.$my.SModal.show({'message':'test', 'delay':1000})
    },
    closeModal: function () {
      this.$my.SModal.hide()
    }
  }
})
.my-modal-class {
  position:absolute;
  top:50px;
  left:20px;
  width:100px;
  height:100px;
  background-color:red;
  z-index:9999;
}
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0a7c7f6f4a38243f243b3c">[email protected]</a>/dist/vue.js"></script>
<div id="app">
    <button @click="openModal()">Open Modal!!!</button>
    <button @click="closeModal()">Close Modal!!!</button>
</div>

Rough Steps for vue-cli project:

In ./plugins/SModal.js (refer to the official documentation to create a Vue instance and add it to the document.body):

let vm = null // the instance for your Vue modal
let timeout = null 

const SModal = {
  isActive: false,

  show ({
    delay = 500,
    message = '',
    customClass = ''
  } = {}) {
    if (this.isActive) {
      vm && vm.$forceUpdate()
      return
    }

    timeout = setTimeout(() => {
      timeout = null

      const node = document.createElement('div')
      document.body.appendChild(node)

      vm = new this.__Vue({
        name: 's-modal',
        el: node,
        render (h) { 
          return h('div', {
            staticClass,
            'class': props.customClass
          })
        }
      })
    }, delay)

    this.isActive = true
  },
  hide () {
    if (!this.isActive) {
      return
    }

    if (timeout) {
      clearTimeout(timeout)
      timeout = null
    } else {
      vm.$destroy()
      document.body.removeChild(vm.$el)
      vm = null
    }

    this.isActive = false
  },

  __Vue: null,
  __installed: false,
  install ({ $my, Vue }) {
    if (this.__installed) { return }
    this.__installed = true
    $my.SModal = SModal 
    this.__Vue = Vue 
  }
}

export default SModal

As per the official documentation, A Vue.js plugin should expose an install method. The method will be called with the Vue constructor as the first argument, along with possible options

In install.js:

// loop all plugins under the folder ./plugins/, then install it.
export default function (_Vue, opts = {}) {
  if (this.__installed) {
    return
  }
  this.__installed = true
  const $my = {
    'memo': 'I am a plugin management.'
  }
  if (opts.plugins) {
    Object.keys(opts.plugins).forEach(key => {
      const p = opts.plugins[key]
      if (typeof p.install === 'function') {
        p.install({ $my, Vue: _Vue })
      }
    })
  }

  _Vue.prototype.$my = $my
}

In main.js:

import install from './install'
import * as plugins from './plugins'

Vue.use({ install }, {
  plugins
})

Finally, in your view/component, you can interact with your modal like this:

this.$my.SModal.show()
this.$my.SModal.hide()

Answer №3

Yes, you can add a property to the modal component:

 properties: ['show']

Make sure to include it when using the modal:

<modal :show="openModalFlag"> ... </modal>

Then check if the modal should be displayed:

<div v-if="displayingModal || show"><slot></slot></div>

Answer №4

To make the component in the children (modal.vue) responsive, I just include v-on="$listeners":

// modal.vue
<template>
   <div :show="show" v-on="$listeners">
     ...
   </div>
</template>

<script>

export default {
    props: {
        show: {
            type: Boolean,
            default: false
        }
    },
    ...

This allows easy opening and closing of the modal from its parent:

//parent.vue
<modal @close="showModal = false" :show="showModal" />

Answer №5

One cannot easily call a method in a component directly, however, there are alternative methods available. You can either modify a property in the child component (such as `show`) or utilize events like Custom Events, $emit, and $refs. Another option is to use an event bus for more complex interactions. These approaches offer different solutions based on the complexity of the interaction needed.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to reset my react-final-form fields led to receiving an empty object as the values instead

Currently, I am facing a situation where I need to clear all field values except for "retainThisObj" upon the initial page load. The problem I encountered is that my { } ended up empty, meaning that my "retainThisObj" was also deleted. ...

Converting a JavaScript function to TypeScript with class-like variables inside: a step-by-step guide

During the process of converting a codebase to TypeScript, I encountered something unfamiliar. In particular, there are two functions with what appear to be class-like variables within them. The following function is one that caught my attention: const wai ...

Choose the current div using JavaScript

Looking to add the selected product along with its quantity and price to another div tag using the jQuery click function. Each time I click, only the first array value of the variable "product" is displayed. How can I access the values of the current row ...

When utilizing Next.js, the router.push feature will automatically scroll the page to the top, even if the scroll option

Incorporating Next.js' built-in internationalisation features allowed me to seamlessly switch my app's language, but there is one specific issue I'm encountering: When I trigger the changeLanguage function, it causes the page to scroll back ...

Prevent removal of h2 tag within a contenteditable segment

Can a section be made permanent within a contenteditable element to prevent user removal? I have an h2 tag inside a contentEditable div. I do not want the user to be able to edit the h2 tag, so I set contentEditable=false, but they can still select and de ...

Tips for displaying only one image when clicked and hiding other divs:

Currently tackling a project that involves JavaScript and I've hit a roadblock. Before you dive into the text, take a look at the image. Check out the picture here. I have successfully created a slider, but now I want to implement a feature where cli ...

Is there a way to modify a document without altering its original location?

I attempted to load an entire page using ajax, with the doctype and html tags removed. However, when I tried setting it with the following code: document.documentElement.innerHTML=xmlhttp.responseText; Google Chrome returned an error message: An invalid ...

Unveil the modules of a Node.js NPM application

I have a Node application that is used as an npm module and serves as a dependency in the package.json file of another Node application. This application needs to grant access to internal modules to the app utilizing my package as a dependency. All these m ...

A guide to implementing offline.js or online.js alongside a submit button

I am looking for a way to check network connection only when the user presses the SUBMIT button, without constantly monitoring for internet connectivity. After researching websites and Stack Overflow questions for weeks, I have not found a satisfactory sol ...

Unable to launch Vue UI on a newly created project

Having just embarked on my second project, I took the necessary steps to set up Vue CLI globally using npm. After creating the project with vue create, I encountered an issue when trying to launch vue ui. Although a new tab opened in my browser, it failed ...

I've recently delved into the world of JavaScript and am currently working on creating a calculator website. However, I'm facing some challenges in getting it to function

I created a calculator code using HTML, CSS, and JavaScript for a website. However, due to my limited experience with JavaScript coding, I encountered some issues. Currently, I have only implemented the number input part (not operations or deletion), but w ...

Getting Anchor Javascript to Submit in Internet Explorer. Functioning in Firefox, Chrome, and Safari

Greetings Stackoverflow enthusiasts, I have been working on a raffle form where users can input their name and select their location from a dropdown list. After filling out the form, they can click submit and their information will be stored in a database ...

The datatable fails to render after executing a function in AngularJS

When I load the page without calling a function, the data is displayed in a datatable perfectly fine. However, if I try to generate the datatable after calling a function, it does not work. HTML: <div class="widget-body no-padding"> ...

How can I ensure the carousel stays centered on a webpage even after resizing the browser window?

Currently in the process of developing a website, I have implemented a jquery-based carousel on the homepage sourced from here. However, substantial modifications were made to tailor its appearance specifically for the site. The issue at hand is that I hav ...

What is the best approach to utilize checkboxes in Laravel PHP to update multiple records simultaneously using AJAX?

Suppose I have two arrays: csid = [1 , 2 , 3] and area_id = 10. I want to assign the area ID to the CS ID. So, I attempted the following code: This code captures all user IDs in the array var ids = [], retrieves the area-select value, and passes it to the ...

Transforming the Slideshow Gallery into a fully automated gallery experience

Looking for assistance in creating an automatic rotation for this slideshow gallery. I've included the HTML, CSS, and JavaScript code for reference. As a newcomer to JavaScript, any guidance on this project would be greatly appreciated. ...

Invalidating the express response object due to a TypeError issue

Currently, I am in the process of writing a test that utilizes sinon and sinon-express-mock to mock an incorrect request. The goal is to then call a validation function within my application and verify that it returns the expected response status code (400 ...

Is $where in MongoDb optimized for better performance when utilizing functions stored in db.system.js?

MongoDb advises limiting the use of $where due to performance concerns, suggesting to use other operators whenever possible. However, an alternative approach is to store Javascript functions on the server side using the special 'system.js' table. ...

Transitioning from Global Namespace in JavaScript to TypeScript: A seamless migration journey

I currently have a collection of files like: library0.js library1.js ... libraryn.js Each file contributes to the creation of a global object known as "MY_GLOBAL" similarly to this example: library0.js // Ensure the MY_GLOBAL namespace is available if ...

Code-behind not functioning properly for Bootstrap Modal

Whenever the password or username are incorrect, I need to open a modal and keep it in the 'Else' statement. However, it is not working, the modal does not open. protected void bntLogar_Click(object sender, EventArgs e) { Registrar c ...