I am working in the project developed with Vue 2 with VueRouter and I am trying to work with my modals controlled by my VueRouter!
I've done the following code
Main vue component: My normal components will be loaded on the default router-view and all my modals will be loaded on the modal router-view
<div id="app"> <router-view v-if="!isLoading"></router-view> <router-view v-if="!isLoading" name="modal"></router-view> </div>
RoutedModals Mixing As you can see on my beforeRouteEnter method I am checking if there is a previous "from" route (which means the user got the page navigating inside the app)... If it's I set that one as default component... if not (which means the user got directly from the URL) I set my dashboard as default and it will be opened behind my modal.
import Dashboard from 'modules/dashboard/components/Main.vue' import { isNil } from 'lodash' export default { data() { return { canAccessDirect: true, goBackTo: '/' } }, beforeRouteEnter(to, from, next) { to.matched[0].components.default = isNil(from.matched[0]) ? Dashboard : from.matched[0].components.default next(vm => { if (!vm.canAccessDirect) vm.$router.push({ name: 'dashboard.index' }) vm.fetchRecords() vm.goBackTo = from.path window.jQuery(vm.$el).modal('show') window.jQuery(vm.$el).on('hide.bs.modal', () => { vm.$router.push(vm.goBackTo) }) }) }, beforeRouteLeave(to, from, next) { setTimeout(() => { next() }, 200) }, methods: { fetchRecords() { // Do list request } } }
An example of my router object: The first route will open a modal on the router-view modal and the second will open only on the default router-view
{ name: 'leads.quick-add', path: '/leads/quick-add', components: { modal: QuickAdd }, }, { name: 'leads.index', path: '/leads', component: Main, },
It works great! The problem comes when I access my modal URL (does not matter if it's directly or navigating) and the default component has a child component! The child component get away on that case!
There is attached some screenshots to help you out understand what happens...
Image 1
Image 2
At Image 1 we can 2 components where the number 1 is my default component on my VueRouter and the number 2 is his child!
Ar the Image 2, after clicking on the + Quotation button the modal is loaded and the component number 2 getaway!
Any ideas on how to do it keeping the others components?
Just to be clear I want to do it by routing and no calling my modal manually!
########################## Edit I am trying to do something like that instead of check on beforeRouterEnter method:
{ name: 'leads.show.quotations.create', path: '/leads/:id/quotations/create', components: { default: Show, 'default.tab': Quotations, modal: Add }, meta: { requiresAuth: true } },
Where there is a sub-router-view but it does not work!
Thinking about possibilities I've added this issue on the github repo: https://github.com/vuejs/vue-router/issues/1030
The URL /search? q=vue would pass {query: 'vue'} as props to the SearchUser component. Try to keep the props function stateless, as it's only evaluated on route changes. Use a wrapper component if you need state to define the props, that way vue can react to state changes.
Vue Router 4 provides multiple ways of doing this: from setting the props property on the route record to true and automatically passing all params as props, setting the props property to a static object to provide static props, or setting it to a function that returns the desired props.
I did this in a project at work. It is quite simple actually, the hard part lies in mixing the jquery that you have there, and maybe the css to position a modal, since it's entry point is the router-view inside your component I recommend using the package portal-vue to move the modal content to the end of the body.
Here is a working exemple: https://codesandbox.io/s/vue-router-modal-j10t2
First create a Modal Component that receives it's child by props:
<template> <portal to="modal"> <div class="modal-wrapper"> <div class="overlay" @click="$router.back()"></div> <div class="modal"> <!-- you can use v-bind to pass down all props --> <component :is="component" v-bind="$attrs"/> </div> </div> </portal> </template> <script> export default { name: "Modal", props: ["component"], }; </script> <style scoped> .modal-wrapper { position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; } .modal { position: absolute; z-index: 1; margin: auto; left: 50%; top: 50%; transform: translate(-50%, -50%); background-color: white; padding: 5em; border: 1px solid #ccc; border-radius: 1em; box-shadow: 0 0 1em #00000033; } .overlay { z-index: 1; position: absolute; width: 100vw; height: 100vh; background-color: #00000055; } </style>
Then you can use the props in routes to assign the content for a given route:
import Home from "./Home.vue"; import Modal from "./Modal.vue"; import Content from "./Content.vue"; const router = new VueRouter({ routes: [ { path: "/", component: Home, children: [ { path: "create", component: Modal, props: { component: Content } } ] } ] });
Since the modal is a child route of '/', the Home component needs to render the router-view:
<template> <div> Hi i'am home <router-view /> <router-link to="/create">open modal</router-link> </div> </template>
And the App.vue needs to render the portal target, so the modal goes to the end of your content (it makes positioning the modal a lot easier):
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png" width="25%" /> <router-view /> <portal-target name="modal" /> </div> </template> <script> export default { name: "App", }; </script>
And that's it
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With