Reputation: 51
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.
Upvotes: 3
Views: 1201
Reputation: 6788
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.
Upvotes: 2
Reputation: 685
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.
Upvotes: 4