Search code examples
javascriptvue.jsvuejs2async-awaitv-for

Using a method to get array to iterate through with a V-for loop, but nothing is being displayed


I am making a personal project to learn how Vue.js and Express communicate together. I managed to get the desired data from the server by passing a prop in the Vue template (brand of watch to be fetched from the server) as an argument to a method that fetches the data.

This method is being called in the V-for declaration and returns an array of objects. Now, when I test this with a standard array that I instantiated inside the data function all is well, and the data displays just fine. The method definitely fetches the data because I can see it inside Vue devtools and when I print it to the console. So it is there, but for some reason it doesn't want to play nice with V-for.

Here's the code:

<template>
    <div class="wrapper">
      <div class="product" v-for="(product, index) in getProductsByBrand(brand)" :key="index">
        <div class="product-name">
          {{ product }}
        </div>
      </div>
    </div>
</template>
<script>
import ProductService from '@/services/ProductService'
export default {
  name: 'product',
  props: [
    'brand'
  ],
  data () {
    return {
      products: [],
      shoppingItems: [
        { name: 'apple', price: '7' },
        { name: 'orange', price: '12' }
      ]
    }
  },
  methods: {
    async getProductsByBrand (brand) {
      const response = await ProductService.fetchProductsByBrand(brand)
      this.products = response.data.product
      console.log(this.products)
      console.log(this.shoppingItems)
      return this.products
    }

When I replace the getProductsByBrand() method with the shoppingItems array it all works. That method returns another array, so I don't see why V-for is having trouble with displaying that data.

Any help is much appreciated!


Solution

  • Vue templates are rendered by a render function, and it's a normal sync function. Therefore you can't call an async function in the template. A better pattern for your code would be as follows:

    Template:

    <div class="product" v-for="(product, index) in products" :key="index">
    

    Script:

    methods: {
      async getProductsByBrand (brand) {
        const response = await ProductService.fetchProductsByBrand(brand)
        return response.data.product
      }
    },
    async created() {
      this.products = await this.getProductsByBrand(this.brand);
    }
    

    It amounts to the same thing but loads the data into the products data property during the created lifecycle hook.