Hello everyone!
In this tutorial we are going to learn how to display a Vue route as a modal, similar to image links on Dribble.com or Google contacts. This format is very useful when you want to show information to the user without redirecting them to another page
This tutorial assumes you are familiar with Vue.js.
First lets create a basic vue project with vue-router
vue-create $folder
For this project I also installed faker.js to create dummy data for our contact list
npm install --save faker
Then I created the following files
src/db.js (Mock database)
import faker from 'faker'
const contacts = {}
for (let index = 0; index < 10; index++) {
const id = "user_" + index
contacts[id] = {
id,
name: `${faker.name.firstName()} ${faker.name.lastName()}`,
jobTitle: faker.name.jobTitle(),
email: faker.internet.email(),
phone: faker.phone.phoneNumber(),
}
}
export default {
contacts
}
src/views/Contacts.vue (Contact list)
<template>
<div class="contacts">
<h2>Contacts</h2>
<div class="contact-list">
<router-link
tag="div"
class="item"
:to="`/contacts/${contact.id}`"
v-for="contact in contacts"
:key="contact.id"
>
<strong>{{contact.name}}</strong>
<br />
<span>{{contact.jobTitle}}</span>
</router-link>
</div>
</div>
</template>
<script>
import db from "@/db.js";
export default {
computed: {
contacts() {
return Object.values(db.contacts);
}
}
};
</script>
<style lang="scss" scoped>
.contact-list {
.item {
&:not(:last-child) {
margin-bottom: 8px;
}
padding: 16px;
border-radius: 4px;
border: solid 1px #e2e2e2;
}
}
</style>
src/views/ContactInfo.vue
<template>
<div class="contact-info">
<router-link to="../">X</router-link>
<h3>{{contact.name}}</h3>
<h4>Job Title</h4>
<span>{{contact.jobTitle}}</span>
3
<h4>Email</h4>
<span>{{contact.email}}</span>
<h4>Phone</h4>
<span>{{contact.phone}}</span>
</div>
</template>
<script>
import db from "@/db.js";
export default {
props: ["contactId"],
data() {
return {
contact: ""
};
},
created() {
this.contact = db.contacts[this.contactId];
}
};
</script>
<style lang="scss" scoped>
.contact-info {
padding: 16px;
}
</style>
I also modified the src/router.js
file to add a route for /contacts
that points to the Contacts.vue component. I am also redirecting the root path to /contacts
After making these changes we get this:
If we click on a user the route will display a blank screen. To fix this we need to add a route for the ContactInfo component. We need to create the route as a child of the /contacts
route.
src/router.js
import ContactInfo from './views/ContactInfo.vue'
...
{
path: '/contacts',
component: Contacts,
// ADDED
children: [
{
path: ':contactId',
component: ContactInfo,
props: true
}
]
},
...
Next we need to add a router-view
component to Contacts.vue
...
<div class="contacts">
...
...
<router-view></router-view>
</div>
</template>
Now click a Contact and you will see this at the bottom of the screen:
This is not exactly what we want. We need to make the router-view show as a modal. We have to make a few changes
Go to Contacts.vue
and replace <router-view></router-view>
with this
...
<div v-if="showModal" class="modal-route">
<div class="modal-content">
<router-view></router-view>
</div>
</div>
...
We are moving router-view inside a "Modal" wrapper which will only be display when the showModal
property is set to true
Then add this to style
section
.modal-route {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background: rgba($color: #000000, $alpha: 0.5);
.modal-content {
width: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
}
And finally in the script
section, we add watcher for the $route property and create a showModal
property inside our data. showModal
is set to false to prevent the modal from being displayed when we first load the page.
...
watch: {
$route(newVal, oldVal) {
this.showModal = newVal.meta && newVal.meta.showModal;
}
},
data() {
return {
showModal: false
}
},
Every Vue component has a $route
property that contains information about the current route. (More information on the $route
object Here). By watching the $route
property we can be notified when the current route changed.
Next open src/router.js
and add a meta
property to the /contacts/:contactId route
...
component: Contacts,
children: [
{
path: ':contactId',
component: ContactInfo,
props: true
meta: {
showModal: true
}
},
...
In Vue.js the meta
property is used to pass additional information to the route. meta
is not to be mistaken with the props
property. More info on meta
here
Now click on a contact and you should see the modal
The new route is now /contacts/user_0 and the contact info is displayed in the same page.
If you refresh the page you will see that the modal is not displayed even thought the route points to /contacts/:contactId
The router-view
modal is only displayed when showModal
is set to true. The problem is that Vue does not call a watcher by default when a component is created and since showModal
is set to false when the component is rendered, the modal is not visible. To fix this we need to refactor our $route
watcher like this:
src/Contacts.vue
$route: {
immediate: true,
handler: function(newVal, oldVal) {
this.showModal = newVal.meta && newVal.meta.showModal;
}
}
The handler
property is the function that is executed when a changes occurs to the property we are watching. immediate:true
tells Vue to check the watched property $route
when the component is created. More info on watched here
Now refresh the page and the modal should be displayed properly.
I hope you enjoyed this tutorial. The full source code is available on Github ! And a demo is a working demo is available at http://friendly-brain.surge.sh
Top comments (2)
thanks
404 on the Github link