Tarvo Mäesepp
Tarvo Mäesepp

Reputation: 4533

Array is sometimes empty in Vue.js if getting data from API

I am trying to make component that needs object array to function. I am passing it from parent to child using props.

I am getting the data from API using axios.

Since the objects in the array may not contain required key "is_selected" then I am adding it to every object in array. But the problem is that the array is sometimes empty.

I have tried everything I could possibly find. The only way it works, is with static data.

I created sandbox also and there you can see in the console that the "COMPONENT DATA" is empty array for some reason but if you assign static objects to the array, it works flawlessly

So this is what I am doing in my child:

<template>
 <ul>
 <li v-for="item in items">{{item.title}}</li>
 </ul>
</template>

<script>
export default {
  props: {
    items: {
      type: Array,
      default: []
    }
  },
  data() {
    return {
      local_items: this.items
    };
  },
  methods: {},

  created() {
    var result = this.local_items.map(function(el) {
      var o = Object.assign({}, el);
      o.is_selected = false;
      return o;
    });
    this.local_items = result
    console.log('COMPONENT DATA: ', this.local_items);
  }
};
</script>

And this is the parent:

<template>
 <test :items="items"></test>
</template>

<script>
import Test from "./Test";
import axios from 'axios'
export default {
  data() {
    return {
      items: []
    };
  },
  methods: {},

  async created () {
    try {
      axios.get('https://jsonplaceholder.typicode.com/posts')
        .then(response => {
          this.items = response.data
        })
    }catch (e){
      console.log('ERROR: ', e)
    }
  },

  components: {
    Test
  }
};
</script>

What I am doing wrong? Can anyone please lead me to the right track?

Edit: if you are looking this, the working example is in the sandbox

Upvotes: 1

Views: 3412

Answers (1)

Quasdunk
Quasdunk

Reputation: 15230

Posting comment as answer

The problem here is that within your child component you only process your data once when the child component is created and the created lifecycle hook is called. That's cool as long as

  • the data is passed directly into the component while constructing it (i. e. it's 'hard coded', for instance when the markup is rendered on the server-side) and

  • the passed in data never changes.

But in most cases you'll either fetch the data dynamically via Ajax or it will change during the lifetime of the component (or both).

In your case, you fetch the data dynamically from a parent component and then pass it to your child component. But the logic to process it is only called once when the created lifecycle event fires. So it won't affect your data

  • if the data is not yet available during the creation of the component (which is quite likely, because you can't predict when an Ajax call will be finished) or

  • if the data changes during the component's lifetime.

So instead of hooking into a fixed lifecycle event you could either

  • fire your own event from the parent as soon as it has fetched the data and hook into it from your child (which can get quite messy on a larger scale)

  • watch the child component's properties for changes and then do what's necessary

So basically, you just want to call your logic from a watch-handler instead of the created-handler:

<script>
export default {
  props: {
    items: {
      type: Array,
      default: []
    }
  },

  data() {
    return {
      local_items: this.items
    };
  },

  watch: {
    /* this is called every time the items prop changes */
    items: function() {

      var result = this.local_items.map(function(el) {
        var o = Object.assign({}, el);
        o.is_selected = false;
        return o;
      });
      this.local_items = result
      console.log('COMPONENT DATA: ', this.local_items);
    }
  }

};
</script>

Upvotes: 1

Related Questions