Vue - pass prop via router to component - javascript

I have a component with the object "project_element" and I want to transfer the object to another component through the "vue-router".
This is the code from my first component which opens the second component if the user clicks on the button.
<router-link :to="{ name: 'project', params: { project_url: project_element.project_name, project_element: project_element} }">
<b-button> Open </b-button>
</router-link>
This is the code from my Vue Router in index.js
{
path: '/projects/:project_url',
component: SingleProjectViewApp,
name: 'project',
props: { project_element: project_element }
},
I already managed to set the "project_element.project_name" to the url but I also need the "project_element" itself in my second component.
In the compenent I have set the object in the "props section"
props: {
project_element: {
type: Object,
required: true
}
},
The problem is in the Vue Router, I can't pass the project_element like a variable, only with quotation marks. But then I get an error because obviously the component expected an object and not a string.
Thanks for your help!

Try this in your router
path: '/projects/:project_url?',
component: SingleProjectViewApp,
props(route) {
const props = {
projectElement: route.params.project_url
};
return props;
}
and in your props change "project_element" to "projectElement" (generally you want to do camel case in vue props)
props: {
projectElement: {
type: Object,
required: true
}
},

The first thing that came to my mind is JSON.stringify() and JSON.parse(). Although I am not sure this is a perfect solution.
Any way try this:
<router-link :to="{ name: 'project', params: { project_url: project_element.project_name, project_element: JSON.stringify(project_element)} }">
<b-button> Open </b-button>
</router-link>
// router.js
{
path: '/projects/:project_url',
component: SingleProjectViewApp,
name: 'project',
props(route) {
return {
project_element: JSON.parse(route.params.project_element),
}
}
},
Note: I honestly think this is an antipattern and you should use different aproach for comunicating through components. custom-events, centralized-state, event-bus or even provide & inject will all be better for that kind of work.

routes: [
{
path: '/',
name: 'start',
component: Start,
meta: {
my_data: "my data here",
},
},]
to get data inside the component
this.$route.currentRoute.meta.my_data
UPDATE
this.$route.push("/"+ JSON.stringify(data) )
routes: [
{
path: '/:data',
name: 'start',
component: Start,
},]
to get data inside the component
JSON.parse(this.$route.params.data)

Related

Deep nested routes and different components rendering, based on route path

I have deep nested routes in my routes.js file. As you can see in code bellow I have to render different component, based on route (if route is products I need to render Products.vue component, but if route goes deeper I need to render EmptyRouterView.vue component which contains template <router-view></router-view> so I can render sub route components).
{
path: '/products',
name: 'products',
component: {
render(c) {
if (this.$route.name === 'products') {
return c(require('pages/Products/Products.vue').default)
} else {
return c(require('components/EmptyRouterView.vue').default);
}
}
},
meta: {
requiresAuth: true,
allowedPositions: '*'
},
children: [
// Scan product to get info
{
path: '/products/search-product',
name: 'search-product',
component: () => import('pages/Products/SearchProduct.vue'),
meta: {
requiresAuth: true,
allowedPositions: '*'
}
},
....
]
}
I wonder if there is some short or better way to do this? For example (I know I can't call this in arrow function) something like this?
component: () => {
this.$route.name === 'products' ? require('pages/Products/Products.vue').default : require('components/EmptyRouterView.vue').default
}
Or do you see if there is possible to do this some completely other way?
If you need any additional informations, please let me know and I will provide. Thank you!
You can i.e. create another .vue-file and include both components inside (<cmp-1 /> & <cmp2 />). Then you can build your if-statement inside the template with another template-tag:
<template v-if="boolean">
<cmp-1 />
</template>
<template v-else>
<cmp-2 />
</template>
The if depends on your route then.

Passing props from one component to another when the transition happens using router.push

I currently have a Vue.js component called Summoner.vue which is rendered thanks to the router when a user visits the following URL - http://localhost:8080/summoner/username
In that component I have a <div> element which triggers a method on click, that sends the user to a new URL - http://localhost:8080/summoner/username/match/4132479262 which renders a different component Match.vue. Like this:
<div #click='specificMatch(match.gameId)'>
specificMatch(gameId){
router.push('/summoner/' + this.summoner + '/match/' + gameId)
}
Now all I want to do is pass an object as props from the first component to the second one, but I'm not sure how to do that because I'm using the router. Normally I'd pass props like this - <summoner v-bind:match="match.id"></summoner> but I guess that doesn't work in my case since I'm using router.
And these are my routes:
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/summoner/:summonerName',
name: 'summoner',
component: Summoner
},
{
path: '/summoner/:summonerName/match/:matchId',
name: 'match',
component: Match
}
]
})
Here is how you can do it with vue-router props :
{
path: '/summoner/:summonerName',
name: 'summoner',
component: Summoner,
props : true // now you can pass props to this route
}
then when you want to navigate to it :
this.$router.push({ name : summoner , params : { summonerName : this.summoner , somedata : 'hello' etc ... }})
now summoner component will have access to all these params on its props :
// summoner.vue
export default {
props : ['somedata',...]
...
}

How can I pass a variable when calling a route?

I want to display the customer's name in a new vue page(from Messages.vue to CustomerMessages.vue). I don't know how to pass this variable into the child vue.
I have tried props but it doesn't work as I expected.
{ path: '/messages/:id/customers', component: CustomerMessages, name: 'CustomerMessage', beforeEnter: requireAuth }
this.$router.push({ name: 'CustomerMessage', params: { id: item.ID }, data: {name: this.customers.FullName} })
{{name}}
props:{
name: ''
}
Vue-router doesn't let you pass arbitrary data to routes like that.
If you want a parent component to share some data with a child route, then you can just bind it on the <router-view> component like you would with any other component prop.
Parent
<router-view :name="name"></router-view>
data() {
return {
name: 'Alice'
}
}
Child
{{ name }}
props: ['name']

Basic Vue help: Accessing JS object values in component

I’ve been experimenting with vue.js and I'm having difficulty accessing JS object values in components when routing.
Using this repo to experiment, https://github.com/johnayeni/filter-app-vue-js, I'm just trying to replicate a basic a “product list” and “product description” app, but I can't get it working. The repo's homepage (the SearchPage.vue component) serves as the "product list," and I'm just trying to add the "product description" component to display only one item at a time.
I've added a "description page" component (calling it "item.vue") to allow a user to click on one of the languages/frameworks that will then route to item.vue to just display that specific object's associated information (item.name, item.logo, etc.), i.e., and not display any of the other languages.
Following some tutorials, here's what I've tried:
First, I added ids to the JS objects (found in data/data.js), i.e., id:'1'.
const data = [
{
id: '1',
name: 'vue js',
logo: 'http://... .png',
stack: [ 'framework', 'frontend', 'web', 'mobile' ],
},
{
id: '2',
name: 'react js',
logo: 'http://... .png',
stack: [ 'framework', 'frontend', 'web', 'mobile' ]
},
...
];
export default data
Then, I wrapped the item.name (in ItemCard.vue) in router-link tags:
<router-link :to="'/item/'+item.id"> {{ item.name}} </router-link>
I then added a new path in router/index.js:
{
path: './item/:id',
component: item,
props: true
}
But, when that router-link is clicked I can only access the ".id" (via $route.params.id), but I can't get .name or .logo. How do I access the other values (i.e. item.name, item.logo, etc.)? I have a feeling I'm going down the wrong track here.
Thank you so much for your help.
The only reason you have access the id because it's an url param: ./item/:id.
You have a couple options here, which depends on what you're trying to accomplish:
As suggested by #dziraf, you can use vuex to create a store, which in turn would give you access to all the data at any point in your app:
export default {
computed: {
data() {
return this.$store.data;
}
}
}
Learn more here: https://vuex.vuejs.org/
As an alternative, you can just import your data, and grab the correct item by its id:
import data from './data.js';
export default {
computed: {
data() {
return data.find(d => d.id === this.$route.params.id);
}
}
}
Just depends on what you're trying to do.
I guess you just need a wrapper component that takes the desired item from the URL and renders the proper item. Let's say an ItemWrapper:
<template>
<item-card :item="item"></item-card>
</template>
<script>
import ItemCard from './ItemCard.vue';
import data from '../data/data';
export default {
components: {
ItemCard,
},
props: {
stackNameUrl: {
required: true,
type: String,
},
},
data() {
return {
item: {},
}
},
computed: {
stackName() {
return decodeURI(this.stackNameUrl);
}
},
created() {
this.item = data.find( fw => fw.name === this.stackName);
}
}
</script>
<style>
</style>
This component takes a prop which is a stack/fw name uri encoded, decodes it, finds the fw from data based on such string, and renders an ItemCard with the fw item.
For this to work we need to setup the router so /item/vue js f.i. renders ItemWrapper with 'vue js' as the stackNameUrl prop. To do so, the important bit is to set props as true:
import Vue from 'vue';
import Router from 'vue-router';
import SearchPage from '#/components/SearchPage';
import ItemWrapper from '#/components/ItemWrapper';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'SearchPage',
component: SearchPage
},
{
path: '/item/:stackNameUrl',
name: 'ItemWrapper',
component: ItemWrapper,
props: true,
},
]
});
Now we need to modify SearchPage.vue to let the stack boxes act as links. Instead of:
<!-- iterate data -->
<item-card v-for="(item, index) in filteredData" :key="index" :item="item"></item-card>
we now place:
<template v-for="(item, index) in filteredData" >
<router-link :to="'/item/' + item.name" :key="index">
<item-card :key="index" :item="item"></item-card>
</router-link>
</template>
So now every component is placed within a link to item/name.
And voilá.
Some considerations:
the :param is key for the vue router to work. You wanted to use it to render the ItemCard itself. That could work, but you would need to retrieve the fw from data from the component created(). This ties your card component with data.js which is bad, because such component is meant to be reusable, and take an item param is much better than go grabbing data from a file in such scenario. So a ItemWrapper was created that sort of proxies the request and pick the correct framework for the card.
You should still check for cases when an user types a bad string.
Explore Vue in depth before going for vuex solutions. Vuex is great but usually leads to brittle code and shouldn't be overused.

VueJS component display and pass item to component

I am using VueJS 2.0 and vue-router 2 and am trying to show a template based on route parameters. I am using one view (WidgetView) and changing components displayed in that view. Initially I show a widget list component (WidgetComponent), then when the used selects a widget or the new button in in the WidgetComponent in the WidgetView I want to swap the WidgetComponent out and display the WidgetDetails component, and pass information to that component:
WidgetComponent.vue:
<template>
...
<router-link :to="{ path: '/widget_view', params: { widgetId: 'new' } }"><a> New Widget</a></router-link>
<router-link :to="{ path: '/widget_view', params: { widgetId: widget.id } }"><span>{{widget.name}}</span></router-link>
</template>
<script>
export default {
name: 'WidgetComponent',
data() {
return {
widgets: [{ id: 1,
name: 'widgetX',
type: 'catcher'
}]}
}
}
</script>
WidgetView.vue
<template>
<component :is="activeComponent"></component>
</template>
<script>
import WidgetComponent from './components/WidgetComponent'
import WidgetDetail from './components/WidgetDetail'
export default {
name: 'WidgetView',
components: {
WidgetComponent,
WidgetDetail
},
mounted: function () {
const widgetId = this.$route.params.widgetId
if (widgetId === 'new') {
// I want to pass the id to this component but don't know how
this.activeComponent = 'widget-detail'
}
else if (widgetId > 0) {
// I want to pass the id to this component but don't know how
this.activeComponent = 'widget-detail'
}
},
watch: {
'$route': function () {
if (this.$route.params.widgetId === 'new') {
// how to pass id to this compent?
this.activeComponent = 'widget-detail'
}
else if (this.$route.params.widgetId > 0){
// how to pass id to this compent?
this.activeComponent = 'widget-detail'
}
else {
this.activeComponent = 'widget-component'
}
}
},
data () {
return {
activeComponent: 'widget-component',
widgetId: 0
}
}
}
</script>
WidgetDetail.vue
<template>
<option v-for="manufacturer in manufacturers" >
{{ manufacturer.name }}
</option>
</template>
<script>
export default {
props: ['sourcesId'],
...etc...
}
</script>
router.js
Vue.use(Router)
export default new Router({
routes: [
{
path: '/widget_view',
component: WidgetView,
subRoutes: {
path: '/new',
component: WidgetDetail
}
},
{
path: '/widget_view/:widgetId',
component: WidgetView
},
]
})
I couldnt get route paramers working but I managed to get routes working by hard coding the route ie
<router-link :to="{ path: '/widget_view/'+ 'new' }"> New Widget</router-link>
But I dont know how to pass an id to the given template from the script (not template) code in WidgetView.
Here is a basic example http://jsfiddle.net/ognc78e7/1/. Try using the router-view element hold your components. Also, use props inside components to pass in variables from the URL. The docs explain it much better http://router.vuejs.org/en/essentials/passing-props.html
//routes
{ path: '/foo/:id', component: Bar, props:true }
//component
const Bar = { template: '<div>The id is {{id}}</div>',props:['id'] }
Not sure which way you want it, but you could have the /foo/ path actually be the creation widget and then have the dynamic /foo/:id path. Or you could do like I did here and the foo path is like a start page that links to different things.

Categories

Resources