Create a modal window with Vue.js custom directives

Emmanuel
4 min readAug 11, 2019

--

So I’m that guy that thinks Vue.js can build a spaceship. But let’s build a modal window instead cos it’s way cooler! I had to do this on a project where I couldn’t use bootstrap so I thought to share my solution. We’ll explore Vue.js custom directives and refs to create a solution quite similar to what we are used to with bootstrap modals so let’s get to it!

First, let’s scaffold our page. I have copied some code from a w3schools example on how to create a modal window with bootstrap.js but for this example, we’ll take the bootstrap styles and leave out the js so we can build our own modal from scratch with Vue. You can get the starter code here.

Bootstrap CSS comes with a few useful styles for modals so we’ll keep most of those. However, we need to override a few things.

By default the modal class has a display: none attribute so we have added a show class with display: block. Down the line, we will dynamically add or remove the show class to open and close our modal window.

Now let’s initialize Vue.js and add a data attribute with which to control the show class.

Then we bind the show class to the data attribute we’ve created with Vue.

There are a few ways we can go from here and we could add an @click event to the open and close modal buttons but what if we had several modals on this page? It would be nice to create a separate component for each one with their individual data attributes which we could then manipulate separately. So let’s turn our modal window into a component.

Notice that our component has a slot which allows us the flexibility to put anything we want within our modal. All we need at this point is a way to control when our modal opens or closes. This is where we get to use refs and custom directives. We will first uniquely identify each modal component using a ref and then take control of them in our directives like so:

Custom directives can have 3 hook functions bind, inserted and update. Here we use the bind hook function to determine what happens when our custom directive gets attached to the element. We passed 3 values into our bind hook function. el, binding and vnode.

  1. el represents the element to which the directive is attached. In our bind functions, we are adding an on click event listener to every element our directives get attached to.
  2. binding holds whatever values passed into the custom directive. In our case we pass in the ref we attached to the modal component we wish to control. We then retrieve this from binding.value.
  3. vnode is the virtual node produced by Vue’s compiler. It holds the current Vue.js context among other values.

You can learn more about Vue.js directive hook arguments here.

In our directives, we get the current context from vnode. We then find the desired modal component from the list of $refs that exist on our Vue.js instance using the binding value passed into the directive. This gives us an instance of the referenced v-modal component.

For v-modal-open, we set the show data property to true and then we add an overflowY: hidden style to the body element to make the modal scrollable. Similarly, for v-modal-close, we set the show data property to false and then we add an overflowY: auto style to the body element to make the rest of the page scrollable when the modal is hidden. Now once we attach our directives to any element they can be used to control any modal on the page by passing in its ref.

Our solution works very similar to data-toggle and data-target in bootstrap. However, we have leveraged the awesome powers of Vue.js custom directives and it wasn’t that hard! We also get the freedom to give our modals whatever look we want. You could also add your own CSS transitions to make it a little fancier :). Hopefully, you find the ideas here useful. You can find the code for this example here and also a working example. Cheers!

Working Example

--

--

Emmanuel

Hi, welcome to my medium page. I'll be posting some of the cool stuff I've learnt as a web developer on frameworks like Laravel, VueJS, Angular and more…