Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access api data in child component on ready with Vue.js

I've working on a big application and I'm having a lot of trouble dealing with data from my API and passing it onto my child components.

The situation.

I'm calling my API from the parent component and passing the data through a prop to my child component. The child component displays the data just fine, but I'm not able to access the data within the ready function in my child component.

Take a look: https://jsfiddle.net/kmrfkynf/3/

As you can see in the console, displaying the data within the child components ready function gives me a empty object...

ready: function(){
    console.log('items from child component', this.items);
}

... but the child components renders the object just fine within my repeat.

So the problem is that the child component is rendered before the API call from the parent is complete. When its complete, it syncs the data to my child component, thus rendering just fine.

What I've tried

Watching the prop from within my child component. When the prop is 'complete' I can access it. But this gives me lots of problems when trying to modify some data in the prop, because it renders every time. This is not the solution I want.

The question

How can I make sure that the prop is filled when the child component is ready?

like image 656
Martijn.Veenstra Avatar asked Dec 11 '15 09:12

Martijn.Veenstra


2 Answers

The problem is that the DOM is loaded and ready while the AJAX function is still receiving the data. So the ready method on the component fires before the results come back from the server.

You could fire an event to tell the child when the data is back from the server:

var item = Vue.extend({
    props: ['items'],
  data:function(){return{items:[]}},
  template: '<div v-for="item in items">{{ item }}</div>',
  ready: function(){

    this.$on('datahere', function(){
        alert(this.items[0].id);
      //console.log('datahere items', this.items);

    })
    //console.log('items from child component', this.items);
  }
})

Vue.component('item', item);

var items = Vue.extend({
    ready: function(){
    this.init();
  },
  data: function(){
    return {
        items: {}
    }
  },
  methods:{
    init: function(){
        this.getItems();

    },
    getItems: function(){
        this.$http.get('https://api.github.com/users/mralexgray/repos')
        .success(function(data){
          this.$set('items', data);
          this.$nextTick(function(){
                this.$broadcast('datahere', data);
          });

          console.log('items from parent components: ', this.$get('items'));
        })
    }
  },
    template: '<div class="test"><item :items.sync="items"></item></test>',
  components: {'item': item}
})

Vue.component('items', items);



var app =  new Vue({
    el: '#app'
})
like image 105
Douglas.Sesar Avatar answered Nov 20 '22 18:11

Douglas.Sesar


As @Douglas.Sesar said, the ready method on the item component fires before the results come back from the server.

To fix that, all you need to add is the activate hook:

var item = Vue.extend({

  activate: function(done) {
    var unwatch = this.$watch('items', function() {
      unwatch(); // teardown the watcher
      done(); // ready to insert the component
    });
  },

  props: ['items'],
  template: '<div v-for="item in items">{{ item }}</div>',
  ready: function(){
    console.log('items from child component', this.items);
  }
})

That hook is called before component insertion. The component will not be inserted until the done callback is called. The done callback is called when the items changed (see watch for details). Hence, the items will have the correct data when the ready function is called.

https://jsfiddle.net/kmrfkynf/8/

like image 42
Pantelis Peslis Avatar answered Nov 20 '22 17:11

Pantelis Peslis