1

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

4
  • 1
    You assume that the data from an asynchronous (!) call will be available until a certain point in time, namely until the created lifecycle hook is called within the child component. But you can't possibly tell when the ajax call from the parent will finish and pass the data to the child. It can be before or after the created hook. So instead you want to watch your prop in the child for changes and then run your data processing. Commented Apr 25, 2018 at 7:16
  • @Quasdunk Thank you for explaining. The given idea worked perfectly. Commented Apr 25, 2018 at 7:42
  • Glad it worked :) I'll just post it as an answer since I think it's a common caveat especially when starting with Vue, so others may find it helpful. Commented Apr 25, 2018 at 7:56
  • Yeah, I think you should add it as answer also, since it is pretty common thing I think :) Commented Apr 25, 2018 at 8:23

1 Answer 1

1

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>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.