It appears that router property must be present at the time of Vue instance construction
new Vue({router}).$mount("#app")
I like to do the following
var App = Vue.extend();
// later in the code...
App.router = router;
new App().$mount("#app")
Getting an error
Vue warn]: Error when rendering root instance:
Uncaught TypeError: Cannot read property 'matched' of undefined
Why do you need to add the router later on?
I have a server rendered multi tenant pager with SPA part. And that page contain Vue instantiation code common for all clients (including ajax wireups). Each client specific SPA parts are loaded dynamically. And routing is only applicable in some of those cases.
The extended component is a constructor. You can just do:
App.options.router = router
Thanks! Didn't notice that indirection!!
I would like to do this too, except after the creation of the component.
@posva, I'm looking to adopt vue-router incrementally. I've been looking through the docs and searching, trying to find a way to inject the router after component creation.
We've implementing the router in an admin dashboard for an application, and there's a component acting as the root component of the admin dashboard. That component is bundled in along with the rest of the app's code.
Something like this:
<template>
<div class="admin-dashboard">
</div>
</template>
<script>
import routes from 'routes/dashboard';
import VueRouter from 'vue-router';
export default {
name: 'admin-dashboard',
router: new VueRouter({ routes })
}
</script>
Now, due to the VueRouter being instantiated at time of import, that VueRouter is instantiated even if the component is not used. So on every other page, VueRouter automatically changes the page's hash to #/ when it's instantiated.
I could set a global, or check the page URL etc. before instantiating VueRouter, but I'd prefer if I could set up VueRouter in some lifecycle hook so that the instantiation doesn't depend on a global, or the URL, but instead whether or not the component is even used. It just feels like a better solution.
I've tried doing:
beforeCreate() {
// Dynamically add in router
this.$options.router = new VueRouter({
routes
});
}
But doing that presents me with the following error in the console:
TypeError: Cannot read property 'matched' of undefined
at render (vue-router.esm.js:68)
at createFunctionalComponent (vue.js:4054)
at createComponent (vue.js:4244)
at _createElement (vue.js:4414)
at createElement (vue.js:4351)
at vm._c (vue.js:4483)
at Proxy.render (kiosk.vue?8edf:131)
at VueComponent.Vue._render (vue.js:4535)
at VueComponent.updateComponent (vue.js:2788)
at Watcher.get (vue.js:3140)
Is there a way to do this? If not, checking the URL or a global is fine, I'd just rather the creation of VueRouter be implicit as a result of creating the admin-dashboard component in the first place.
Thanks in advance!
I'm using https://single-spa.js.org/ and I want to dynamically load a router into the root Vue instance. I'm doing something similiar to @CaelanStewart and I'm getting:
TypeError: Cannot read property 'matched' of undefined
Caelan, did you manage to get it to work?
Has anyone managed to do this? Our Vue app is packaged as a web component that gets included on a separate web page. We're looking for a way to init the router in the connectedCallback on our web component (which I believe equates to mounted in Vue). Doesn't seem to be a way to do this.
I managed to get this to work by setting: app.$options.router directly on the wrapper application
@espiegel I've tried your suggestion but I'm getting the same error as @CaelanStewart .
I've tried in my sub component that wants a router by trying this:
beforeCreate() {
this.$parent.$options.router = getRouter();
}
I've tried both this.$parent and using a reference to the wrapper Vue instance.
The getRouter function looks like this (in other module);
let router;
export function getRouter() {
if (router) { return router; }
Vue.use(VueRouter);
router = new VueRouter();
return router;
};
Did you put that on the root vue instance?
I also managed to put an empty new VueRouter({}) on the root instance and then dynamically call:
app.$router.addRoutes(routes); with a routes array
@espiegel The question is; when in the lifecycle did you assign app.$options.router ?
I tried this basically;
// app.js
var app = new Vue({ ... });
window.$app = app;
app.$mount('#app');
// in component registered into app.
beforeCreate() {
console.log(window.$app); // jackpot!
window.$app.$options.router = getRouter();
}
@espiegel Basically from what I've gathered; the issue is that <router-view> is trying to use $route-property of the parent component that renders it.
And $route and $router gets assigned as properties here; https://github.com/vuejs/vue-router/blob/dev/src/install.js#L38-L44
The issue is that if you don't assign the router to the options before instantiating the root Vue instance those properties will point to the value assigned by this line https://github.com/vuejs/vue-router/blob/dev/src/install.js#L29 which will be this meaning the component itself. Which will not have any _routerRoot property.
But since this is a closed ticket, I'll stop here! :)
@CaelanStewart I was wrestling with the same thing for a bit today. It's interesting that all tutorials assume you want the router on your root instance and you're not building a mini SPA that's part of a larger site. My main problem was the undesired hash in the URL on pages that weren't using vue-router, although I get what you're saying about not wanting to load it at all if it's not even used on the current page. My solution for the hash was just to use the mode: 'history' option. But I think you could solve your problem using an async component. I realize you've probably moved on by now so I guess this is for posterity.
components: {
'component-with-router': () => import('./component-with-router')
}
@regularmike
That's great, thanks for the suggestion.
If I'm honest we opted for the lazy route and forgot about it, since there's so much on the backlog still to get through.
I'll look into the async component option, though.
Thanks
@regularmike I can confirm it works with dynamic components
Allthough instead of using ajax to get the component i still get in only when i need it using require
'component-with-router': () => require('./component-with-router')
@regularmike
could u please show an example how exactly you are doing this async thing? I dont get where goes what.
I remove router from main vue instance
this.vueInstance = new Vue({
el: this.rootElement,
i18n,
store,
// router
});
and then how do I add it to a component on a specific page load ?
I have checked the docs but they also show it like one line
components: {
'my-component': () => import('./my-async-component')
}
but where does this code go? and how do I append router to vue instance in './my-async-component'?
I didnt know my understanding of vue was this bad :S
thanks
Most helpful comment
I would like to do this too, except after the creation of the component.
@posva, I'm looking to adopt vue-router incrementally. I've been looking through the docs and searching, trying to find a way to inject the router after component creation.
We've implementing the router in an admin dashboard for an application, and there's a component acting as the root component of the admin dashboard. That component is bundled in along with the rest of the app's code.
Something like this:
Now, due to the VueRouter being instantiated at time of import, that VueRouter is instantiated even if the component is not used. So on every other page, VueRouter automatically changes the page's hash to
#/when it's instantiated.I could set a global, or check the page URL etc. before instantiating VueRouter, but I'd prefer if I could set up VueRouter in some lifecycle hook so that the instantiation doesn't depend on a global, or the URL, but instead whether or not the component is even used. It just feels like a better solution.
I've tried doing:
But doing that presents me with the following error in the console:
Is there a way to do this? If not, checking the URL or a global is fine, I'd just rather the creation of VueRouter be implicit as a result of creating the
admin-dashboardcomponent in the first place.Thanks in advance!