2

I have a dynamic page that loads product details, but the html is loaded before the data.

So when I try to use static elements like an image I get an error stating the object "product" does not exist.

To fix this I gave every dynamic element v-if="product != undefined" which does work, but doesn't seem like a very good way of solving this.

I'm initiating my data through the store like this

In my page i do:

async mounted() {
  await this.fetchProducts()
},
computed: {
  product() {
    return this.$store.state.products.producten.filter(product => product.id == this.$route.params.id)[0]
  }
}

Then in my store:

export const state = () => ({
  producten: []
})

export const mutations = {
  setProducts(state, data) {
    state.producten = data
  }
}

export const actions = {
  async fetchProducts({ commit }) {
    await axios.get('/api/products')
      .then(res => {
        var data = res.data
        commit('setProducts', data)
      })
      .catch(err => console.log(err));
  }
}

I tried replacing mounted() with: beforeMount(), created(), fetch() but none seemed to work.

I also tried:

fetch() {return this.$store.dispatch('fetchProducts')}

Loader(v-if="$fetchState.pending")
Error(v-if="$fetchState.pending")
.product(v-else)
  // Product details...
kissu
  • 40,416
  • 14
  • 65
  • 133
Marnix Elling
  • 335
  • 1
  • 3
  • 15
  • 2
    What if you try `v-if="product.length"` (checking if you have a populated array or not basically). If you have an empty array, the condition in `v-if` will be falsy and will not display anything. Also, prefer to use `async/await` in your store, rather than `.then` (which is deprecated). At the end, you need to wait for your data to be populated, while your template is synchronous and expects to not loop on something empty. [$fetchState.pending](https://stackoverflow.com/a/67862314/8816585) is a really good solution. Also [`asyncData`](https://nuxtjs.org/docs/features/data-fetching#async-data). – kissu Dec 14 '21 at 14:09
  • Why would I use `v-if="product.length"` over `v-if="product != undefined"` is it better? Because they both work. I just feel like its a pretty ugly way of solving this. – Marnix Elling Dec 14 '21 at 14:24
  • 1
    Because product will never be `undefined` looking at what the [filter method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) returns. Another solution would be to use [isEmpty](https://lodash.com/docs/4.17.15#isEmpty) from lodash. Usually, having a basic falsy condition is enough. And it's ugly because the type coercion in JavaScript is meh and we're lacking a real core library for those kind of things. – kissu Dec 14 '21 at 14:32

1 Answers1

4

You could use the fetch hook to dispatch fetchProducts:

<script>
export default {
  fetch() {
    return this.$store.dispatch('fetchProducts')
  }
}
</script>

In your template, use the $fetchState.pending flag to prevent rendering the data elements until ready:

<template>
  <div>
    <Loader v-if="$fetchState.pending" />
    <Error v-else-if="$fetchState.error" />
    <Product v-else v-for="product in products" v-bind="product" />
  </div>
</template>

demo

tony19
  • 125,647
  • 18
  • 229
  • 307
  • I don't get an error but my dynamic elements are not being rendered but the rest of the page is which is also inside `.product`. I've edited my question with the new code. Please note its written in pug. – Marnix Elling Dec 14 '21 at 14:20
  • @MarnixElling we will probably need to know the actual state of your app here. Maybe you have an error somewhere? Do you have a [repro] or a github link maybe? – kissu Dec 14 '21 at 14:23
  • 1
    @MarnixElling The little blurbs you're posting isn't enough to reproduce the problem. Can you edit the demo link in the answer to create a reproduction? – tony19 Dec 14 '21 at 14:32
  • @MarnixElling Indeed, could be nice if you could replicate the issue somewhere here rather than blocking us synchronously and asking to go to your project and fix the thing on our day-time. – kissu Dec 14 '21 at 14:36
  • @tony19 I have recreated the problem its acting the same on the demo now. But I'm not 100% sure if its because of the same reason since I found it a bit hard to understand the structure from the data send by the API that's used here. – Marnix Elling Dec 14 '21 at 14:44
  • 1
    Alright so I have renamed my store file to index.js instead products.js. then I edited my computed property to work with the new name and for some reason that fixed it. Which is very weird because the object did work before because I could see it in vue-devtools. – Marnix Elling Dec 14 '21 at 14:55