0

How do I avoid needing to create a fake this in order to access the components data property? Because I have a new function that is passed to the Array.forEach the this keyword is in fact referring to the function rather than the Vue instance.

created () {
    this.items.push({
        value: 1000,
        selected: false
    });

    var otherThis = this;
    someAjaxOperation.returnedJson.forEach(function (item, index) {
        otherThis.items.push({
            selected: false,
            value: item
        })
    });
}

As you can see, I have the hacky workaround of creating a copy of this. Is there a better way?

user9993
  • 5,833
  • 11
  • 56
  • 117
  • Does https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback?rq=1 not help explain some possibilities? I really wouldn't call your solution a 'hacky workaround' at all. – Cody G Dec 01 '17 at 15:43
  • Except it's not a duplicate, given this is specifically using Vue which has it's own way of working with `this`. – user9993 Dec 01 '17 at 15:45
  • @user9993 No, Vue is not use the `this` in special manner. –  Dec 01 '17 at 15:59
  • I wouldn't say Vue has its "own way of working with `this`". Vue does a bit of magic to set `this` to be a reference to the Vue instance in its methods, watchers, computed definitions, and lifecycle hooks, but that's it. The ways to access the correct `this` inside a callback which you've defined in a Vue hook are going to be exactly the ways described in the linked post. – thanksd Dec 01 '17 at 15:59
  • @thanksd OK thanks for explaining that. Do you happen to have a link where I can read how Vue does that magic? – user9993 Dec 01 '17 at 16:02
  • There's no magic here. The issue is referencing the context in which the callback function is defined. Vue is irrelevant. – Bert Dec 01 '17 at 16:07
  • @user9993 Bert is correct that Vue is not affecting `this` in the callback function. As far as how, technically, Vue is setting the initial value of `this` in the `created` method to the Vue instance, I'm not sure and not immediately able to find any reference explaining it. You could maybe dig through the code on Github. – thanksd Dec 01 '17 at 16:16

1 Answers1

0

One easy way to always get access to the vue instance is via the variable you used when declaring it.

So for example if you have code like this:

   var app = new Vue({
        el: '#app',
        data: {
              someVar: 1
        }
    });

Then you can always get access to someVar via app.someVar

So your example becomes:

 created () {
     this.items.push({
         value: 1000,
         selected: false
     });


     someAjaxOperation.returnedJson.forEach(function (item, index) {
         app.items.push({
             selected: false,
             value: item
         })
     });
 }
RonC
  • 31,330
  • 19
  • 94
  • 139
  • This is a very interesting approach. I wonder how this would affect the reactivity in general, I don't think Vue would be able to track the array being changed that way would it? I've found that arrow functions work for my need, and to support legacy browsers you basically have to make a copy of `this`. – user9993 Dec 01 '17 at 15:53
  • This approach won't affect reactivity at all. It will still work as expected. In fact you can even use this approach from non vue code to change vue data and have it reactively update the page. It's quite handy. – RonC Dec 01 '17 at 15:55
  • 2
    I see this approach often here and it is generally a bad idea. It will only work in extremely simple situations where the entire app is defined in the same scope in which the Vue is constructed (where `app` is defined) unless you start doing weird things like exposing `app` globally. I think this is a bad practice. – Bert Dec 01 '17 at 16:20
  • @Bert - I'm not sure why exposing app globally is weird per se, how is doing so any different than exposing $ globally for jquery? – RonC Dec 01 '17 at 20:27
  • Well, consider for a moment that in the first two lines the code in your answer, you've committed one of the primary sins with global variables; you overwrote an existing global variable (all elements with an ID are exposed as variables in the global scope by that id in browsers, so `var app = ` overwrites the existing `app` global variable that points to `#app`). Global variables are bad because this kind of thing is easy to unknowingly do. – Bert Dec 01 '17 at 20:46
  • Beyond that many tutorials and many people are using single file components and using vue-cli to set that up and telling them to use `app` means they will have to come up with some way to do that in the components, which means its not `var app` but `window.app`, because the component and the Vue are defined in different scopes. I'd rather teach people how to properly understand scope and `this`. – Bert Dec 01 '17 at 20:49
  • Fair enough, those are good points. And honestly, I read his question fast and initially missed that he was in a component rather than the main vue instance since the code example of he provided for the `created` event didn't show what it was part of. My bad. If I had seen it was part of a component I might not have said to use app as a way to get access to the data properties. But I don't see a big issue with naming the global vue object `app` and also using `app` as the id of the main vue template. But I do hear your point. Thanks for explaining. – RonC Dec 01 '17 at 20:58