📜 ⬆️ ⬇️

Creating a modal component using Vue.js

In this article, you will learn how to create a reusable component of a modal window with Vue.js using animated transitions and slots.

Pattern structure definition


Let's start by defining our template. We need a div for the background (shadow), a div for the modal window itself, and some elements to define its structure:

<template> <div class="modal-backdrop"> <div class="modal"> <slot name="header"> </slot> <slot name="body"> </slot> <slot name="footer"> </slot> </div> </div> </template> 

Pay attention to the use of slots? We could use input parameters (props) to create a header (header), body (body) and footer (footer), but using slots will give us more flexibility.
')
Slots allow us to easily use the same modal window with different types of body content of our component. We can use a modal window to show plain text, but we may want to reuse the same modal window for form output to send a request. Although input parameters (props) are usually sufficient to create a component, providing HTML through an input parameter will require us to use it via the v-html directive for rendering - which can lead to XSS attacks .

Here we use named slots, it gives the chance to use more than one slot in one component.

When we define a named slot, everything that we identify with this name will be displayed instead of the original slot — we will call this source slot the default value, as placeholder in input.

Like placeholder, a slot can also have default content that will be displayed if we don’t provide it.

Because the provided content completely replaces the ‹slot› tag to ensure that our header, body, and footer sections have the required classes, we need to wrap each slot in the appropriate element with the necessary classes.

Let's set some default values ​​for slots, their wrappers, and initial CSS to make it all look like a basic modal window.

 <script> export default { name: 'modal', methods: { close() { this.$emit('close'); }, }, }; </script> <template> <div class="modal-backdrop"> <div class="modal"> <header class="modal-header"> <slot name="header"> This is the default tile! <button type="button" class="btn-close" @click="close" > x </button> </slot> </header> <section class="modal-body"> <slot name="body"> I'm the default body! </slot> </section> <footer class="modal-footer"> <slot name="footer"> I'm the default footer! <button type="button" class="btn-green" @click="close" > Close me! </button> </slot> </footer> </div> </div> </template> <style> .modal-backdrop { position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(0, 0, 0, 0.3); display: flex; justify-content: center; align-items: center; } .modal { background: #FFFFFF; box-shadow: 2px 2px 20px 1px; overflow-x: auto; display: flex; flex-direction: column; } .modal-header, .modal-footer { padding: 15px; display: flex; } .modal-header { border-bottom: 1px solid #eeeeee; color: #4AAE9B; justify-content: space-between; } .modal-footer { border-top: 1px solid #eeeeee; justify-content: flex-end; } .modal-body { position: relative; padding: 20px 10px; } .btn-close { border: none; font-size: 20px; padding: 20px; cursor: pointer; font-weight: bold; color: #4AAE9B; background: transparent; } .btn-green { color: white; background: #4AAE9B; border: 1px solid #4AAE9B; border-radius: 2px; } </style> 

And we made a very simple version of the modal window component!

Adding Animated Transitions


Notice how the modal window opens abruptly? We can make a more smooth window I / O using an animated transition.

Vue provides a component transition shell, which allows us to add animated transitions for the appearance and disappearance of any HTML element or component Vue, and allows us to use both CSS classes and JavaScript hooks.

Each time a component or element wrapped in a transition element is inserted or removed, Vue checks if the element has CSS transitions and will add or delete them at the right time. The same is true for JavaScript hooks, but for our case we will use only CSS.

When an item is added or removed, six classes are applied to the I / O transition. Each of them will have a prefix of the name of the transition. In this guide, you will find a detailed explanation of how transitions work.

First add the ‹transition› element to our modal window:

 <template> <transition name="modal-fade"> <div class="modal-backdrop"> <div class="modal"> ... </div> </div> </transition> </template> 

Next we add CSS classes to change the transparency - for the smooth appearance / disappearance of our window:

 <style> .modal-fade-enter, .modal-fade-leave-active { opacity: 0; } .modal-fade-enter-active, .modal-fade-leave-active { transition: opacity .5s ease } </style> 

Now our modal window component opens and closes smoothly!

Making the modal window more accessible.


The only thing missing is to turn our component into a more accessible one for people with disabilities.

We can achieve this using aria attributes.

Adding the role = “dialog” will help the ancillary software to identify our component as a dialog (modal) application window, which is separate from the rest of the user interface. Although adding the role of a dialogue is useful, it is not enough to make it accessible, we need to flag it accordingly. We can achieve this through aria-labelledby and aria-describedb y attributes. And do not forget to mark our close buttons too!

The final version of our modal component should now look like this:

 <script> export default { name: 'modal', methods: { close() { this.$emit('close'); }, }, }; </script> <template> <transition name="modal-fade"> <div class="modal-backdrop"> <div class="modal" role="dialog" aria-labelledby="modalTitle" aria-describedby="modalDescription" > <header class="modal-header" id="modalTitle" > <slot name="header"> This is the default tile! <button type="button" class="btn-close" @click="close" aria-label="Close modal" > x </button> </slot> </header> <section class="modal-body" id="modalDescription" > <slot name="body"> I'm the default body! </slot> </section> <footer class="modal-footer"> <slot name="footer"> I'm the default footer! <button type="button" class="btn-green" @click="close" aria-label="Close modal" > Close me! </button> </slot> </footer> </div> </div> </transition> </template> <style> .modal-backdrop { position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(0, 0, 0, 0.3); display: flex; justify-content: center; align-items: center; } .modal { background: #FFFFFF; box-shadow: 2px 2px 20px 1px; overflow-x: auto; display: flex; flex-direction: column; } .modal-header, .modal-footer { padding: 15px; display: flex; } .modal-header { border-bottom: 1px solid #eeeeee; color: #4AAE9B; justify-content: space-between; } .modal-footer { border-top: 1px solid #eeeeee; justify-content: flex-end; } .modal-body { position: relative; padding: 20px 10px; } .btn-close { border: none; font-size: 20px; padding: 20px; cursor: pointer; font-weight: bold; color: #4AAE9B; background: transparent; } .btn-green { color: white; background: #4AAE9B; border: 1px solid #4AAE9B; border-radius: 2px; } </style> 

Using the modal window component in our application


Now we can use our component by including it in our application. You can also try the component in action here - codepen .

 <script> import modal from './components/modal.vue'; export default { name: 'app', components: { modal, }, data () { return { isModalVisible: false, }; }, methods: { showModal() { this.isModalVisible = true; }, closeModal() { this.isModalVisible = false; } }, }; </script> <template> <div id="app"> <button type="button" class="btn" @click="showModal" > Open Modal! </button> <modal v-show="isModalVisible" @close="closeModal" /> </div> </template> 


PS This article is a translation of this foreign one. In the comment below, I explained how it happened.
PSS Made the translation of the article human.

Source: https://habr.com/ru/post/349306/


All Articles