Adorno
Adorno

Reputation: 51

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.

Upvotes: 3

Views: 1201

Answers (2)

Sergeon
Sergeon

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

geoctrl
geoctrl

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

Related Questions