Optional Parameter on Vue Router - vue.js

So I have a project Page and when I click in one project I want to go to Project Detail page of that project. so I'm using dynamic routes. I pass as parameters a name(that will show on URL) and Id(that I use to go to my store and get the project, this Id is not showing on URL). Everything is ok, the problem is when I Load the browserinside the project Detail page, I only get name as a parameter and I cant get the Id so because of that I cant match the Id with my vuex because there is no Id.
this is my router-link on parent
<router-link class="project__icon" :project="project" :to="{ name: 'projectDetail', params: {name: project.name.replace(' ', ''), id: project.id} }">
and this is what I do on other page to get my project Id inside Vuex
const projectId = this.$route.params.id;
this.project = this.$store.getters.getProject(projectId);
This is what I get when I click on router
http://localhost:8080/project/myprojectName
On my router file I have this
{
path: '/project/:name',
name: 'projectDetail',
component: ProjectDetail,
},

I'm not sure why you are binding project to the router-link, shouldn't be necessary.
<router-link class="project__icon" :to="{ name: 'projectDetail', params: {name: project.name.replace(' ', ''), id: project.id} }">
You need to add :id as a parameter to your route. You can mark it optional with a ?.
{
path: '/project/:name/:id?',
name: 'projectDetail',
component: ProjectDetail,
}
Also, if you set props: true you can decouple your component from the router and instead of using this.$route.params.id, your detail component will receive the parameters as props.
{
path: '/project/:name/:id?',
name: 'projectDetail',
component: ProjectDetail,
props: true,
}
EDIT: You have to have the id in the url if you want to allow refreshing and still keep the id or you have to store it somewhere like localstorage or sessionstorage.

Related

Optional route params with Vue Router

In my Vue 2.7.5 app (using Vue Router 3.5.4) I'm trying to define this route
{
path: '/messages/:messageId?/replies/:replyId?',
name: 'messages',
component: () => import('#/views/messages')
}
The intent is
to see all messages use /messages
to see a specific message use /messages/:messageId
To see a specific message and a specific reply to that message use /messages/:messageId/replies/:replyId
However, if I navigate to this route without specifying any route params using
<router-link :to="{name: 'messages'}">
Then the URL is resolved as /messages/replies, but I would like it to be resolved as /messages.
Essentially, what I want is: don't include /replies unless there's a replyId param, but I don't know how to express that.
One solution is to use the following instead:
<router-link :to="{ path: '/messages'}">
But I prefer to always refer to routes by name, because this gives me the flexibility to change the paths without breaking anything
The easiest solution for you is to remove /replies and only have path like this:
'/messages/:messageId?/:replyId?'
(Optional solution)
If removing that part of url is not an option and using named routes is a must, here is an alternative solution where you use two named routes. If the replyId is missing you can redirect before enter to the 2nd named route.
{
path: '/messages/:messageId?/replies/:replyId?',
name: 'message-replies',
component: () => import('#/views/messages'),
beforeEnter({ params }) {
if (!params.replyId) {
return {
name: 'messages',
params: {
messageId: params.messageId,
},
};
}
},
},
{
path: '/messages/:messageId?',
name: 'messages',
component: () => import('#/views/messages'),
},

Router link is throwing error when navigating away from route which contains a param vue js 3

I am using Vue JS 3 and Vue Router. I have a company area of the app that uses a dynamic companyId parameter in the route. Ex. myapp.com/46/tasks where 46 is the companyId. Everything works fine when I navigate around to the different sub areas of the company area. However, if I am displaying a router link on any page, and that router link depends on the companyId parameter, if I try to navigate anywhere outside of the company area, which does not require the companyId, the reactivity of the router-link throws an error and the navigation does not happen. If I'm located at the route referenced above, and I try to navigate to
<router-link v-if="session.availableAccounts.length > 1" :to="{name: 'selectCompany'}">
{{ session.selectedAccount.name }}
</router-link>
Here is the router-link that throws the error: (however this happens on any page, with any router-link that requires parameters from the existing page and I then try to navigate somewhere without passing in the parameters EVEN THOUGH THE PARAMETER IS NOT NEEDED FOR THE ROUTE I AM TRYING TO GO TO)
<router-link
:to="{
name:'users',
query: {
selected: person.id,
area: 'Info'
}
}">
{{ person.name }}
</router-link>
Here is the portion of my router.js file concerning the 2 routes I am trying to move between.
{
path: '/account',
component: Base,
meta: {
authorization: true
},
children: [
{
name: 'newAccount',
path: 'new',
component: NewAccount,
meta: {
authorization: true,
title: 'New Account'
}
},
{
name: 'selectCompany',
path: 'selectAccount',
component: SelectCompany,
meta: {
authorization: true,
title: 'Select Account'
}
},
{
name: 'createCustomer',
path: 'create',
component: NewCustomerAccount,
meta: {
authorization: true,
title: 'Create Account'
}
}
]
},
{
path: '/:companyId',
component: Base,
meta: {
authorization: true,
nav: 'account'
},
children: [
{
name: 'home',
path: 'tasks',
alias: '',
component: TaskManager,
meta: {
title: 'My Tasks'
},
},
...
]
}
This happens no matter what method I use to cause navigating, whether I use a router-link or whether I call router.push() in code. However the error always comes from a router-link. If I hide all router-links on the page the navigation works flawlessly. I tried to recreate this on a smaller scale app and I can't seem to make it happen, which means I am doing something wrong but I can't figure it out. I also can't find any similar issues here, which is typically a good indicator that I'm doing something wrong. There is definitely a work-around, where I can store that companyId in a Vuex store and pass it around in the route, but why should I have to pass in a parameter that is not actually in the route?! I really don't want to go down that route (pun intended) unless I absolutely have to. And I first ran into this problem with a child route of the company which needs a projectId parameter. I had the same issue when navigating away from /[:companyId]/[:projectId]/anywhere to /[:companyId]/anywhere IF and only if there is a router-link displayed on the page that relies on [:projectId], and in that situation I was actually relying on whether or not projectId existed within the route params to control a navigation menu. I developed a work around for that behavior but otherwise passing the projectId into the router push to keep the error from happening would have stopped my nav menu from updating correctly.
Is the problem that I do not explicitly define the dynamic route in the parameter? It seems like explicitly defining it would solve my problem but it also requires me to store that somewhere, effectively duplicating the data. I would rather have the id defined in one place (the route) rather than storing it in the store and the route and having to worry about keeping them in sync with each other. Is there no other way?
Any help is appreciated. Thanks.
As is normally the case when I ask a question I discover the answer while asking it. Just posting in case anyone else runs into this same issue. The solution is just to make sure that you explicitly provide the dynamic param when you declare the router-link. Not sure if I like that it lets you create the link without a warning that the required param has not been declared (while there is a warning if vue-router can't resolve the route).
My revised router-link:
<router-link
:to="{
name:'users',
params: {
companyId: route.params.companyId
},
query: {
selected: person.id,
area: 'Info'
}
}">
{{ person.name }}
</router-link>

Vue Router: How to pass data to component being linked to?

I am using client side routing and have the route name be the object's name. I am linking to the Edit.vue component but if I want to render the age in that Edit component, how do I get that passed in? I know I have name accessible in the router params but I want the other fields in that object too, such as age.
App.vue
<div v-for="item in items">
<router-link :to="`/edit/${item.name}`"> Edit ${item.name} </router-link>
</div>
data() {
return {
items: [ {name: "Carl", age: 23}, { name: "James", age: 43}]
}
}
then in my router config, I have:
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/edit/:name",
name: "Edit",
component: () =>
import(/* webpackChunkName: "edit" */ "../views/Edit.vue"),
},
];
State management is the keyword, which might help for your further research.
Especially vuex, as the most popular Vue state management library probably makes sense in your case (https://www.npmjs.com/package/vuex). There are tons of tutorials out there.
If you don't want to use a state management library, you can implement a simple version of it on your own by saving the data in the localstorage or in the cookies. Or - if it's just about the age, you also could add it to the query params.
There is also a pretty well described SO answer for a similar question: Vue: shared data between different pages
This is not the best way to do it.
You could implement /edit/:name/:age. But what happens if you access the URL /edit/Carl/999?
You should fetch the data, such as name and age, by a unique user id instead: edit/:userid.

Where did I go wrong with vue-router configuration, or the problem is somewhere in my component?

I have list of users which I output in Home vue component. Every item in the list is coming from vuex and has it's own details. When I click any of this contacts list items vue-router takes me to route /contact/that-item-id for example contact/4536475. Now, when I am on that page for specific contact list item and refresh my browser vue app breaks, in other words I don't have access to that specific item object properties anymore.
Here is the code of my router
export default new Router({
routes: [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/contact/:id",
name: "ContactDetails",
props: true,
component: ContactDetails
I am setting props property to true so I can pass it as params to contact item details component as so:
<router-link
class="view-more-btn"
:to="{ name: 'ContactDetails', params: { id: contact.id }}"
>VIEW DETAILS</router-link>
and at last I am passing that Id to my getters method in vuex to get details for clicked item as this:
export default {
props: ["id"],
computed: {
contact() {
return this.$store.getters.getContactDetails(this.id);
}
}
Where did I go wrong, why I can't refresh my contact item detail page and still preserve state I am using.
I am new to vue so please forgive me if I am not making sence. And ofcourse any help is welcomed, thanks in advance
The problem is probably, that you're referencing a named route and passing in the params by hand. This won't change the actual route displayed in your browsers address bar and only show the root path (/contact/ in your example I presume). Therefore when you refresh the passed in params/props simply don't exist anymore.
What you need to do instead is use a <router-link :to="'/contact/'+contact.id"> or <router-link :to="`/contact/${contact.id}`"">.
This should affect the URL in your browsers address bar to include the /contact/someID123 which will then also make the ID available on refresh.

Get route by name and params for vue-router

I am using Vue with vue-router. For product items in a list view I would like to generate JSON-LN annotations with the url attribute set to the path of the product detail view.
I know I can get the current route's path by using this.$route.path but is there a way to get a distinct route path as it would be rendered with
<router-link :to={name: 'ProductDetail', params: {id: some_id, slug: some_slug}}></router-link>
to inject the route's path somewhere else?
You are looking for the Router instance's resolve method:
Given location in form same as used in <router-link/>, returns object with the following resolved properties:
{
location: Location;
route: Route;
href: string;
}
In your case you could do something like this to get the url:
let props = this.$router.resolve({
name: 'ProductDetail',
params: { id: some_id, slug: some_slug },
});
return props.href;