2

I have a component with some items and those items are loaded from api using get request method. When I click on an item I get redirected to its own page using dynamic routing: { path: '/:id', component: Item }. Clicked item is recognised using currentItem() method: currentItem() { this.items.find(item => item.code === this.$route.params.id) } where item.code is a property I get from api. My problem is that when I refresh the page with current item, it is not loaded anymore. I tried using beforeCreate() to load items one more time in their own component. Maybe I could use a watch to change the state depending on the item?

beforeCreate() {
    this.$http.get(url).then(response => {
          this.items = response.body;
    }
},
watch: {
    '$route' (to, from) {
      this.currentItem()
    }
  }

Here's a demo.

kabugh
  • 305
  • 1
  • 9
  • 30

1 Answers1

1

You should add watch for $route to react to id changes when you navigate between pages. But in this case there is a chance that currentItem return null because your request will have ended after watch handler already invoked.

First solution is to watch items collection in Item component and invoke this.currentItem() in this watch handler. And yes, you will have to load items in your Item component like in your example.

Second one is use computed property currentItem instead method if it's possible:

computed: {
   currentItem() {
       return this.items.find(item => item.code === this.$route.params.id)
   }
}

This will be reactive and you don't need watch anymore. But don't forget to make this.items empty array by default to avoid null errors.

The third solution is combination second one with using Vuex store to share items collection between all components and do something like this:

beforeCreate() {
    // you should check in this action that items already loaded earlier as well
    this.$store.dispatch('loadItems');
},
computed: {
   currentItem() {
       return this.items.find(item => item.code === this.$route.params.id)
   },
   items() {
       return this.$store.state.items
   }
}

Store:

state: {
   items: [],
   itemsLoaded: false,
}
actions: {
   loadItems({state, commit}) {
      // avoid unnecessary loading between navigations
      if (itemsLoaded) return
      Vue.http.get('some url').then(response => {
          commit('setItems', response.body);
          commit('itemsLoaded');
      }
   }
},
mutations: {
   setItems: (state, items) => state.items = items 
   itemsLoaded: (state) => state.itemsLoaded = true
}

So you dont need to store items in both Item and Items components for example.

Sorry for long post.

Max Sinev
  • 5,884
  • 2
  • 25
  • 35
  • I indeed use vuex and have the computed property assigned. What should the `loadItems` action look like? – kabugh Aug 04 '18 at 11:00
  • Your second solution is the one that I currently have and it doesnt work. – kabugh Aug 04 '18 at 11:06
  • @kabugh in second solution do you add loading items in beforeCreate hook? can you update question to see how you cahnge your Item component? I added store example in answer. – Max Sinev Aug 04 '18 at 11:14
  • I also tried to use the solution with vuex actions but still doesnt work. – kabugh Aug 04 '18 at 11:38
  • @kabugh looks like you miss something, I think you should provide more code of your component – Max Sinev Aug 04 '18 at 11:52
  • 2
    @kabugh the main problem is in `id` parameter type, your items has `id` as number type but router return string and you use strict comparison in Product component. Use `parseInt(this.$route.params.id)` or you can pass id as preprocessed prop from router, see https://router.vuejs.org/guide/essentials/passing-props.html#function-mode – Max Sinev Aug 05 '18 at 12:06
  • Yeah, thanks it worked. But how do I recognise which api endpoint should be used depending on clicked item? – kabugh Aug 05 '18 at 13:03
  • @kabugh you can add another router parameter to indicate what type of item is shown now, for example. – Max Sinev Aug 05 '18 at 13:10
  • @kabugh some item type, or you can create another Vue component for special item and add another route for this, so you will have route for special item and item. – Max Sinev Aug 05 '18 at 13:39
  • I thought about creating special component, but fount it inefficient. That's why I am curious if some kind of parameter indicator would work. – kabugh Aug 05 '18 at 13:41
  • @kabugh you can use route `path: "/:type/:id"` and check `type` in Product component. But when user click on item you should specify `type` like this `$router.push(`/special/${item.id}`)` as well. – Max Sinev Aug 05 '18 at 13:45
  • Thank you so much! – kabugh Aug 05 '18 at 13:55