0

I am getting some strange behavior in my Vue application that I am developing.

In my view I define my data initially:

...
data() {
  return {
    organization: {
      selectedOption: null,
      options: [],
    },
  };
},
...

Intention is to populate this via a call to my backend API, which I do using => notation via axios:

// The following snippet is in my methods:
...
axios.get('http://localhost:8000/api/org/types')
  .then((response) => {
    Object.keys(response.data).forEach((k) => {
      this.organization.options.push({
        value: k,
        text: response.data[k],
      });
    });

    this.organization.selectedOption = this.organization.options[0].value;
  });
...

The data comes in, and I can see it indeed does set the values until I go elsewhere within the view.

I initially called the method above in the beforeMount() method however I moved it to the created() method (due to data context/reactivity matters) and all seemed to be working just fine.

Now I am having an issue where when accessing the data where it is always seemingly set to initial data I have defined. I am verifying this via debug/console.

Within mounted():

console.log(this.organization); // Returns observer that shows the data I would expect to be there via Console, but initial data when accessing anything.
console.log(this.organization.selectedOption); // Returns null

Is there something I am not understanding how the Vue data method works? I was under the assumption that after the context has been created the underlying data can then be mutated for the life-cycle of that view.

EDIT: I did attempt to return the promise on the axios call, but to no avail.

4
  • Could you put the code to jsfiddle and tell detailly what you would expect but then got what. Words are way too hard to understand without runnable code. Commented Dec 8, 2019 at 23:36
  • @Huỳnh Lợi Nguyễn Check my comment to skirtle's answer below. Commented Dec 9, 2019 at 0:03
  • I have read the answer below and do enjoy it (of course I upvoted it too). But still don't quite get your problem. I had the general idea that it's about synchronicity, had the basic idea of the answer below but haven't got the connection between the two anyway @@. You've said you solved it using a .then(), but I don't know where have you put it to? Commented Dec 9, 2019 at 0:19
  • 1
    Ah I hate 'thanks, I fixed it' without any details as much as the next guy. My apologies, what I did was within my mounted() method I called my fetching method trailing with .then(() => ... where the data was then handled properly, securing the initial promise. In my situation it was loading graph and stat data from my API. Coinciding with v-if statements on the components, it seems to be working very well with no issues thus far. I also learned of the update and beforeUpdate hooks while reading into it - but I did not try them as my solution seems to be met. Commented Dec 9, 2019 at 21:12

1 Answer 1

3

There are a couple of keys things to note here.

Firstly, when you log an object to the console it is live. You'll probably see a little blue 'i' icon after you expand the object that explains this. What this means is that the object properties are not copied. Instead the console just has a reference to the object. It only grabs the property values when you click on the object in the console to expand it. You can work around this by logging out console.log(JSON.stringify(this.organization)) instead.

The second point to note is that it really doesn't matter which hook you use to load the data. The hooks beforeCreate, created, beforeMount and mounted will all run synchronously at the relevant stages. Meanwhile, your data is being loaded asynchronously. Vue won't wait for it, there's no support for that. No matter which hook you use the data won't be loaded until after the initial rendering/mounting is complete. This is a common problem and you just need to write your component in such a way that it can cope with the data being missing when it first renders.

To be clear, I'm not saying that the hooks are interchangeable in general. They most definitely aren't. It's just that when you're loading data using an AJAX request it doesn't make any real difference which you use. The AJAX request will always come back after all of those hooks have been run. So performing the request in an earlier hook won't make the data available in the later hooks.

A common alternative is to load data in a parent component and only create the child once the data is loaded. That's usually implemented using a v-if and the data is then passed using a prop. In that scenario the child doesn't have to deal with the data being missing.

Sign up to request clarification or add additional context in comments.

1 Comment

I really appreciate your insight here and that makes much more sense to me. I had a feeling it had to do with synchronicity because when creating a JSFiddle (per @Huỳnh Lợi Nguyễn above) the problem was no longer present (see: jsfiddle.net/stevenfloyd/2obp9vfe/1). I also was able to resolve it by using the .then(...) method which further solidifies it as an async issue. I appreciate your help and your understanding here.

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.