8

I have been stuck with this issues for 2 hours now and I really can't seem to get it work.

const app = new Vue({
  el: '#book-search',
  data: {
    searchInput: 'a',
    books: {},
  },
  methods: {
    foo: function () {
      axios.get('https://www.googleapis.com/books/v1/volumes', {
        params: {
          q: this.searchInput
        }
      })
      .then(function (response) {
        var items = response.data.items
        for (i = 0; i < items.length; i++) {

          var item = items[i].volumeInfo;

          Vue.set(this.books[i], 'title', item.title);   

        }
      })
      .catch(function (error) {
        console.log(error);
      });

    }
  }
});

When I initiate search and the API call I want the values to be passed to data so the final structure looks similar to the one below.

data: {
  searchInput: '',
  books: {
    "0": {
       title: "Book 1"
     },
    "1": {
       title: "Book 2"
     }
},

Currently I get Cannot read property '0' of undefined.

Svedr
  • 589
  • 2
  • 6
  • 21
  • Value of `this` is different in the callback function of axios `.then()` method. You have to save the value of `this` outside before using it in callback scope. – abhishekkannojia Jul 02 '17 at 09:26
  • @abhishekkannojia If I change this to `app`, that is how my Vue instance is defined, it throws a `Cannot convert undefined or null to object` error. – Svedr Jul 02 '17 at 09:29
  • Possible duplicate of [Axios can't set data](https://stackoverflow.com/questions/40996344/axios-cant-set-data) – yuriy636 Jul 02 '17 at 10:20
  • Notice that this is about a "traditional" JavaScript object (dictionary), not an actual `Set` object as created with `new Set()` - I came here looking how Vue handles these... – Chris K Aug 05 '19 at 15:22
  • two hours for real. Sigh. – prog_24 Sep 02 '22 at 17:50

3 Answers3

15

Problem lies here:

Vue.set(this.books[i], 'title', item.title);

You are inside the callback context and the value of this is not the Vue object as you might expect it to be. One way to solve this is to save the value of this beforehand and use it in the callback function.

Also instead of using Vue.set(), try updating the books object directly.

const app = new Vue({
  el: '#book-search',
  data: {
    searchInput: 'a',
    books: {},
  },
  methods: {
    foo: function () {
      var self = this;
      //--^^^^^^^^^^^^ Save this
      axios.get('https://www.googleapis.com/books/v1/volumes', {
        params: {
          q: self.searchInput
          //-^^^^--- use self instead of this
        }
      })
      .then(function (response) {
        var items = response.data.items
        var books = {};
        for (i = 0; i < items.length; i++) {

          var item = items[i].volumeInfo;
          books[i] = { 'title' : item.title };
        }
        self.books = books;
      })
      .catch(function (error) {
        console.log(error);
      });

    }
  }
});

Or if you want to use Vue.set() then use this:

Vue.set(self.books, i, {
     'title': item.title
});

Hope this helps.

abhishekkannojia
  • 2,796
  • 23
  • 32
2

yep, the problem is about context. "this" returns not what you expect it to return.

  1. you can use

    let self = this;

  2. or you can use bind

    function(){this.method}.bind(this);

the second method is better.

Also google something like "how to define context in js", "bind call apply js" - it will help you to understand what is going wrong.

1
// update component's data with some object's fields
// bad idea, use at your own risk

Object
  .keys(patch)
  .forEach(key => this.$data[key] = patch[key])
badunius
  • 61
  • 1
  • 5