vue-router route param to component prop? - vue.js

I thought this would work:
{ path: '/course/:id', component: Course.extend({
props: { course: params.id }
}) },
Sadly, it's not that simple (it's not id either). How do I do this? (I just took a course on vue, can't believe I can't remember this)

As it is in the docs, you have to make props option true in the routes, see below code to understand it:
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})

Related

How can I mix Boolean and Object Mode in vue router?

how can I mix Boolean and Object Mode in my Vue Router?:
props: true
with
props: { someRouteSpecificProps: "someValue"}
I need to also send props via router.push.
So:
//router
{
path: "somePath",
name: "someName",
props: { someRouteSpecificProps: "someSpecificValue" },
component: successAndLogoutPage,
},
//component
this.$router.push({
name: "someName",
query: this.$route.query,
params: {
ValueOnlyKnownInCompoent: 500000,
AnotherValueOnlyKnownInCompoent: "foo",
}
},
Using Vue 2, Js not ts.
Doing as I did, will ignore the props from the component. Doing props: true will use the props from the component but I still neet the someRouteSpecificProps directly in the router definition.
The solution is using the function mode:
props: (route) => {
return { someSpecificProps: 'value here', ...route.params };
},
In route the params are provided and can be provided to the props.
(Credit to #PerpetualWar from the vue discord, on whos advice the solution was formed)

No Route name in VueJS 3 + VueRouter 4

I am using VueJS 3 and Vue Router 4. I want to get the name of the current route using {{$route.name}} - this works so far. But it doesn't return any Route Name, if I'm accessing a route - in this Example I am trying to access /plans/1 - it doesn't return any value. here is my routes-array from the router:
const routes = [
{
path: '/plans',
name: 'Learning Plans',
component: ListPlans
},
{
path: '/plans/:id',
name: "Leaning Plan: Learn",
component: ViewPlan,
props: true
},
{
path: '/plans/:id/edit',
name: "Edit Learningplan",
component: EditPlan,
props: true
}
]
What am I doing wrong?
Thanks for every help!
Vue Router 4.x provides useRoute() for that:
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute()
onMounted(() => {
const id = route.params.id
})
}
}
DEMO

Is there a way to remove a directory from a dynamic URL using Vue-Router?

I've build a vue.js web app for an insurance brokerage where every agent has their own website that is generated from their profiles.
This is what the link looks like in my vue-router index file"
{
path: '/agents/:id',
name: 'AgentSite',
component: AgentSite
},
Everything works great EXCEPT that the urls are getting too long to fit on some business cards. I would like to change the URLs to be like this:
{
path: '/:id',
name: 'AgentSite',
component: AgentSite
},
However, then every other bit of dynamic content in the app loads our agent website template (AgentSite). Quotes, Clients, Policies... they won't load properly.
Is there a way to remove the "/agents" from the URLs without messing up the rest of our application? I could shorten it to "/a/:id but that ends up being more confusing than it's worth.
Thanks!
EDIT: a couple of people have mentioned solutions that work when the agent id is a number. That's a great idea except that we have built agent "slugs" to use instead.
On the agent website layout:
created() {
console.log(this.$route.params.id);
this.$store.dispatch("getAgentFromSlug", this.$route.params.id);
}
and in the store:
getAgentFromSlug({commit}, payload){
const db = firebase.database();
db.ref("users/").orderByChild("slug").equalTo(payload).once("value",
(snap) => {
console.log(snap.val());
var info = snap.val();
commit("setAgentSiteInfo", info[Object.keys(info)[0]])
})
}
So, our route Id is really a slug.
Considering ids are numbers, you could use:
{
path: '/:id(\\d+)',
name: 'AgentSite',
component: AgentSite
},
Which only matches if id is made only of numbers.
Update: A couple of people have mentioned solutions that work when the agent id is a number. That's a great idea except that we have built agent "slugs" to use instead.
If the names can conflict with existing routes, declare the agent route last.
From the Matching Priority docs (emphasis mine):
Matching Priority
Sometimes the same URL may be matched by multiple routes. In such a
case the matching priority is determined by the order of route
definition: the earlier a route is defined, the higher priority it
gets.
In other words, declare like:
routes: [
{
path: '/',
component: HomePage
},
{
path: '/quotes',
component: Quotes
},
{
path: '/clients',
component: Clients
},
{
path: '/:id',
component: AgentSite,
props: true
}
]
See CodeSandbox demo Here.
Handling 404 pages
Would I then declare the 404 page route above or below the "AgentSite" in your example? { path: "*", component: PageNotFound }
The AgentSite route would match any URL not matched previously, so you'll have to handle the 404s inside the AgentSite component.
First, declare the 404 route after the AgentSite:
routes: [
// ... (other routes)
{
path: "/:id",
component: AgentSite,
props: true
},
{
path: ":path",
name: "404",
component: p404,
props: true
}
]
Then, inside AgentSite, get the agent :id, check if it is a known agent and, if not, redirect to the 404 route by name (otherwise it would match agent again).
export default {
props: ["id"],
data() {
return {
availableAgents: ["scully", "bond", "nikita"]
};
},
created() {
let isExistingAgent = this.availableAgents.includes(this.id);
if (!isExistingAgent) {
this.$router.push({
name: "404",
params: { path: this.$route.fullPath.substring(1) }
});
}
}
};
The CodeSandbox demo Here already contains this handling.
You can use regex matching if you :id has a specific format (example from vue-router repository).
For example, if your :id is a number:
const routes = [
{ path: '/:id(\\d+)', component: Foo },
{ path: '/bar', component: Bar }
]
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/:id(\\d+)', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
.router-link-active {
color: red;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/321321">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>

vue-router neither watch route or navigation guards firing

Using vue-router in a single page application with the code below, the watch $route function in not firing when redirecting to mycomponent.
Also the beforeRouteUpdate in mycomponent is also not firing.
How can I detect when a variable has been tagged on to a route during component load?
App.vue
<template>
<router-view></router-view>
</template>
<script>
import Vue from 'vue'
export default {
name: 'app'
}
</script>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import MyView from '#/views/MyView'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect: '/home',
name: 'Home',
children: [
{
path: '/mycomponent',
name: 'MyComponent',
component: MyComponentView
},
{
path: '/mycomponent/:id',
component: MyComponentView,
props: true
}
]}]})
mycomponent.vue
<template>
<component :is="activeComponent" :id="id"></component>
</template>
<script>
export default {
name: 'MyComponentView',
components: {
...
},
mounted: function() {
#this logs path in browser
console.log('>>mounted route: ' + this.$route.path)
},
watch: {
'$route': function () {
#this does not fire
console.log('route watcher: ' + this.$route.path)
}
},
beforeRouteUpdate (to, from, next) {
#this does not fire
console.log('>>beforeRouteUpdate')
},
data () {
return {
activeComponent: 'somecomponent'
}
}
}
</script>
component1.vue
...
mounted: function() {
Event.$on('vue-tables.row-click', function(data) {
#this logs correct information in browser
console.log('data.row.id: ' + data.row.id)
router.push({path: 'mycomponent', query: {id: data.row.id}})
})
},
...
It doesn't work because beforeRouteUpdate is in component which is going to reload (Look at Life cycle of Vue). When you change the route, watch & beforeRouteUpdate is terminated and you won't see any results. In this scenario you should provide something like this:
MainRouterView.vue
<template>
<router-view/>
</template>
<script>
name: 'MainRouterView',
beforeRouteUpdate (to, from, next) {
console.log('beforeRouteUpdate')
},
</script>
router.js
export default new Router({
routes: [
{
{
path: '/mycomponent',
name: 'MainRouterView',
component: MainRouterView,
children: [
{
path: '/mycomponent/:id',
component: SecondComponent,
}
]
},
}]})
But if you want to stick up with your structure and check the status of the current route, you can replace beforeRouteUpdate to beforeRouteEnter or beforeRouteLeave in the component. You can use global guard beforeEach in router as well.
To better understand how beforeRouteUpdate works, check out this snippet: http://jsfiddle.net/yraqs4cb/

Passing props to Vue.js components instantiated by Vue-router

Suppose I have a Vue.js component like this:
var Bar = Vue.extend({
props: ['my-props'],
template: '<p>This is bar!</p>'
});
And I want to use it when some route in vue-router is matched like this:
router.map({
'/bar': {
component: Bar
}
});
Normally in order to pass 'myProps' to the component I would do something like this:
Vue.component('my-bar', Bar);
and in the html:
<my-bar my-props="hello!"></my-bar>
In this case, the router is drawing automatically the component in the router-view element when the route is matched.
My question is, in this case, how can I pass the the props to the component?
<router-view :some-value-to-pass="localValue"></router-view>
and in your components just add prop:
props: {
someValueToPass: String
},
vue-router will match prop in component
sadly non of the prev solutions actually answers the question so here is a one from quora
basically the part that docs doesn't explain well is
When props is set to true, the route.params will be set as the component props.
so what you actually need when sending the prop through the route is to assign it to the params key ex
this.$router.push({
name: 'Home',
params: {
theme: 'dark'
}
})
so the full example would be
// component
const User = {
props: ['test'],
template: '<div>User {{ test }}</div>'
}
// router
new VueRouter({
routes: [
{
path: '/user',
component: User,
name: 'user',
props: true
}
]
})
// usage
this.$router.push({
name: 'user',
params: {
test: 'hello there' // or anything you want
}
})
In the router,
const router = new VueRouter({
routes: [
{ path: 'YOUR__PATH', component: Bar, props: { authorName: 'Robert' } }
]
})
And inside the <Bar /> component,
var Bar = Vue.extend({
props: ['authorName'],
template: '<p>Hey, {{ authorName }}</p>'
});
This question is old, so I'm not sure if Function mode existed at the time the question was asked, but it can be used to pass only the correct props. It is only called on route changes, but all the Vue reactivity rules apply with whatever you pass if it is reactive data already.
// Router config:
components: {
default: Component0,
named1: Component1
},
props: {
default: (route) => {
// <router-view :prop1="$store.importantCollection"/>
return {
prop1: store.importantCollection
}
},
named1: function(route) {
// <router-view :anotherProp="$store.otherData"/>
return {
anotherProp: store.otherData
}
},
}
Note that this only works if your prop function is scoped so it can see the data you want to pass. The route argument provides no references to the Vue instance, Vuex, or VueRouter. Also, the named1 example demonstrates that this is not bound to any instance either. This appears to be by design, so the state is only defined by the URL. Because of these issues, it could be better to use named views that receive the correct props in the markup and let the router toggle them.
// Router config:
components:
{
default: Component0,
named1: Component1
}
<!-- Markup -->
<router-view name="default" :prop1="$store.importantCollection"/>
<router-view name="named1" :anotherProp="$store.otherData"/>
With this approach, your markup declares the intent of which views are possible and sets them up, but the router decides which ones to activate.
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
// for routes with named views, you have to define the props option for each named view:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
Object mode
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
That is the official answer.
link
Use:
this.$route.MY_PROP
to get a route prop