Sending props from different routes to the same component using Vue JS - javascript

I'm building a menu app using Vue JS. I was told that only have to use 1 component if the styling stays the same. So that means i have to use dynamic data. each menu/submenu has 3 to 4 menu links. I was looking for a solution to send variables with data to a component and came up with 'props'. But i couldn't find a way to send props from different routes to the same component and check which route you're on to know which props to load into the html.
I already tried to load props into a template, and that works fine. But sending and replacing prop values in the same part of html is something i haven't figured out yet.
{ path: '/list', component: List, props: { url: 'www.google.com' } }
<template>
<div class="container">
<h1>Welkom</h1>
<div class="row">
<div class="col-md-6">
<router-link to='/weed'>Wiet</router-link>
</div>
<div class="col-md-6">
<router-link to='/stuff'>Stuff</router-link>
</div>
<div class="col-md-6">
<router-link to='/joint'>Joint</router-link>
</div>
<div class="col-md-6">
<router-link to='/edibles'>Edibles</router-link>
</div>
</div>
</div>
</template>
the <router-link> should dynamic depending on which route you are, so it can load in different menu items.
I want a menu that loads in different route links depending on which route you are.

I think you should use Programmatic Navigation and query params like
router.push({ path: 'register', query: { plan: 'private' } })
so for your application use
<div class="col-md-6"
#click="$router.push({ path: '/edibles',
query: { url: 'www.google.com', other: 'etc' }})"
>Edibles</div>
you can access these query params in the navigated component using $route.params.url in a template and this.$route.params.url in functions and computed and all other properties of vue component instance.
also check https://router.vuejs.org/guide/essentials/navigation.html for details
Edit as per comment
I think you should still use Programmatic Navigation with
router.push({ name: 'user', params: { userId } })
the params provided can be used as props to the component as shown below
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})
just remember to set props: true in routes definition!

You can use params from your route as props in your component and use a watcher to react to route changes. Either have a state system (vuex) with which you can access specific data regarding the params or just plainly use the params.
Use: router.push({ path: '/user/${userId}' })
then
watch: {
$route(to, from) {
// react somehow
}
}
and/or
template: '<div>User {{ $route.params.id }}</div>'

Related

Vue-router keeps appending # to existing routes. P.S this isn't the typical hash, history mode issue

Using Vue 3, i have my router file set up this way
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/Portfolio",
name: "Portfolio",
component: () =>
import(/*webpackChunkName: "DestinationDetails" */ "../views/Portfolio"),
},
{
path: "/Services",
name: "Services",
component: () =>
import(/*webpackChunkName: "DestinationDetails" */ "../views/Services"),
},
{
path: "/details/:id",
name: "PortfolioDetails",
component: () =>
import(
/*webpackChunkName: "DestinationDetails" */ "../views/PortfolioDetails"
),
},
{
path: "/:pathMatch(.*)*",
redirect: "/Home",
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
I am also running a v-for loop to get paths from an API "https://api.fake.rest/ca2a6662-22d0-4010-ba08-0440ffe813ab/menu". 3 of the 5 url paths have a value of "#". the remaining two have normal paths.
<div
v-for="(men, index) in webMenu.menu_items"
:key="index" class=" mt-32"
>
<!-- <SidebarLink class="w-full" :to="{path:men.url}" icon="fas fa-home">{{
men.name
}}</SidebarLink> -->
<router-link class="w-full" :to="men.url"> {{men.name}} </router-link>
</div>
<p class="font-bold">{{webMenu.menu_text}}</p>
</div>
Problem is now when the webpage loads initially, it works fine but after clicking on the portfolio or services link, the paths to the others are changed.
e.g if i was on the portfolio page and tried switching back to the home page, it would change the route path to "portfolio#" and refuse to switch pages.
Can anyone help explain why this is and a possible way to resolve it?
I was also facing the similar issue.
For me the issue was that I was redirecting to new page using router.push() on click of a div.
And I was using #click.stop = "myFunction" . This was adding a # in the url and not redirecting the user.
I tried different variation of this as well. I tried using event object as well like event.stopPropagation(). With this as well got same result.
When I used #click.prevent, this everything worked as expected.
PS:
You can use catch block to trace the error as well.
this.$router..push({
name: "PAthName",
params: { id },
query: { id }
})
.catch(e => {
console.log("Errors", e);
});
Should have updated this earlier.
The fix was relatively simple, all i did was change the binding from
<router-link class="w-full" :to="men.url"> {{men.name}} </router-link>
to
<router-link class="w-full" :to="`/${men.url}`"> {{men.name}} </router-link>
and voila!

Access Router Params VueJS

I'm creating a blog using Vuejs and I'm fairly new to it.
In short. I have a dynamic list of elements that are loaded onto the screen and when you click on one of the items, I want to go to that page with the rest of the data. I followed somewhat of the same process I did if I was using React.
Router.js
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/post/:id",
name: "PostView",
props: true,
component: PostView
}
]
});
I have my router setup to take dynamic links.
Component That Creates List Data
<template>
<router-link v-bind:to="{ path: 'post/' + post.postId, params: { post } }">
<div> .... </div>
</router-link>
</template>
<script>
import moment from "moment";
export default {
name: "recentPostTile",
props: {
post: Object,
},
};
</script>
As you can see that is how I did my router-link and in my actual Page View this is how I'm trying to read the data. 2 different way's I tried to read it.
props: {
post: Object
},
AND
data() {
return {
postData: this.$route.params.post, (also tried with $route)
};
},
and when I try to console out post/postData I just get undefined. I can assure you that the data does exist since the data is hard coded into the browser.
Also I tried to pass the params as key value pairs as well and that didn't work. I also create a new variable for the data I was going to be passing through the link. That didn't work either. I also haven't tired query, just because I don't want an ugly looking link.
Change your link to:
<router-link :to="{ name: 'PostView', params: { id: post.postId, postData: post } }">
<div> .... </div>
</router-link>
The changes:
Used the name property to specify the route
Removed the hardcoded path
Passed the id param, as that is the param name given in the route definition (/:id)

Render nested routes as individual pages in VueJS

I'm using VueJS's own router component. In my admin interface, there is one page which shows an overview list and another one with specific options for one single entry.
function configRoutes() {
return [
{
path: '/',
redirect: '/default',
name: 'Home',
component: TheContainer,
children: [
{
path: 'admin/viewerRoles',
name: 'List',
component: AdminViewerRoles,
children: [
{
path: ':id-:name',
name: 'Settings',
component: AdminViewerRoleSettings
}
]
}
]
}
]
}
With that approach, I can place a <router-view /> tag in my AdminViewerRoles component. This will render the details page as viewport. What I wonder is, if it is possible to render either of the views without giving up the syntactically correct, nested navigation approach.
My idea would be to place the <router-view /> tag and the normal page content in two different div elements and render them conditionally with v-if. But is that really the best approach?
This would be my idea (untested):
<template>
<div v-if="$route.params.id">
<router-view />
</div>
<div v-else>
<table>...</table>
</div>
</template>

How to pass data to vue router router-view?

Im using Vue Router. In my code I used to have:
<div v-bind:is="page.component_name" v-bind:page="page"></div>
Which worked, and the page data was passed to the component. But how do I do the same with a router-view? This doesn't seem to work:
<router-view v-bind:page="page"></router-view>
js:
var vm = new Vue({
...,
router : new VueRouter({
routes : [
{ path: '/foo', component: { template: '<div>foo</div>', created:function(){alert(1);} } },
//{ path: '/bar', component: { template: '<div>bar</div>', created:function(){alert(2);} } },
{ path: '/bar', component: Vue.component("ti-page-report") }
]
}),
...
});
vue-router has a dedicated page in docs on how to pass props to router-view.
Passing Props to Route Components
Example snippet from docs:
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 }
}
]
})
If you are looking for simplified usage, props can still be passed the same way they are passed to any component. But component that is used for rendering the route (the one that is specified in route definition) should expect to receive the props.
Here is simple usage example of passing props to router-view:
I personally decided to use provide/inject feature: preserving reactivity with minimal overhead.
The component ("ti-page-report") that needs to access the props being sent just needs to add it:
<template>
<div>
<h1>Now you can access page: {{ page }}</h1>
</div>
</template>
export default {
name: "TiPageReport",
props: ['page'], // can now be accessed with this.page
...
};
See https://v2.vuejs.org/v2/guide/components-props.html for how to use props properly.

Deeply nested Angular routes

A the root of my app I have a "demo" route to a module that handles all the demos on the site:
{
path: 'demo',
canActivate: [AuthGuardService],
loadChildren: './demo/demo.module#DemoModule'
}
Then, in the demo module's routing module I have all the demos, some of which are also modules, such as
{
path: 'splitviews',
loadChildren: './splitviews/splitviews.module#SplitviewsModule'
}
Which is a module of demos of types of "Splitviews", components with 2 named route outlets: 1 for the left nav and 1 for the content:
<app-header class="site-header"></app-header>
<div class="site-content"></div>
<div [ngClass]="containerType"
class="splitview-wrapper">
<div class="row">
<div class="col-sm-2"">
<router-outlet name="leftnav"></router-outlet>
</div>
<div class="col-sm-10">
<router-outlet name="content"></router-outlet>
</div>
</div>
</div>
<app-footer class="site-footer"></app-footer>
So far, this module has routing like this
path: 'basic',
component: SplitviewsComponent,
data: {
containerType: 'container'
},
children: [
{
path: '',
component: LeftnavBasicComponent,
outlet: 'leftnav'
},
{
path: '',
component: Content1Component,
outlet: 'content'
}
]
},
So now if you went to /demo/splitviews/basic you'd see the splitviews component with the header, footer, and the 2 route outlets displaying LeftnavBasicComponent and Content1Component.
But now what I need to do is have the links in the LeftnavBasicComponent load different content components (e.g. Content2Component) inside the router-outlet "content". And I'm not sure how to best configure those sub-routes.
You can use the LeftnavBasicComponent and Content1Component as placeholders and then the components you really want to show will be loaded inside them depending on a value you'll pass by url.
Your html will be like below
<app-header class="site-header"></app-header>
<div class="site-content"></div>
<div [ngClass]="containerType"
class="splitview-wrapper">
<div class="row">
<div class="col-sm-2"">
<app-leftnav></app-leftnav>
</div>
<div class="col-sm-10">
<app-content></app-content>
</div>
</div>
</div>
app-leftnav and app-content are respectively the selectors of LeftnavBasicComponent and Content1Component.
The routing module will simply look like below:
{
path: 'basic/:leftnavtype/:contenttype',
component: SplitviewsComponent,
data: {
containerType: 'container'
}
}
Now in the LeftnavBasicComponent typescript file we have to take the value of leftnavtype from the url and put it in a variable like below:
leftNavType: string;
ngOnInit() {
this.route.params.subscribe((params: Params) => {
this.leftNavType = params["leftnavtype"];
});
}
And finally in the LeftnavBasicComponent html file we show a specific component depending on the leftNavType value
<app-type-1-left-nav *ngIf="leftNavType==='type1'"></app-type-1-left-nav>
<app-type-2-left-nav *ngIf="leftNavType==='type2'"></app-type-2-left-nav>
The same will be done for the content placeholder.
After doing so, if you call /demo/splitviews/basic/type1/content3, the type1 component will be loaded in the left nav placeholder and the content3 component will be loaded in the content placeholder, and you can then generate the link you want in the navigation part to load a specific component in the content part.
It may not be the perfect solution, but it works in your case.
And if there is only few cases, I suggest simply creating a route for each one, both solutions work depending on your needs.
Happy to clarify more if needed!

Categories