4

I have a computed property that will only be used if a match for a property exists. Because of this, I'm making the call to get the data asynchronous so that it's only retrieved when needed. I'm having an issue though trying to make an async call to return data for a computed property.

Below is what I have:

new Vue({
    el: "#formCompleteContainer",
    data: {
        form: {},
        components: []
    },
    computed: {
        employeeList: function () {
            var self = this;
            if (_.some(this.components, function (component) {
                return component.ComponentInfo.Type === 8
            })) {
                var employees = [];
                $.ajax({
                    url: "/Form/GetAllUsers",
                    type: "GET"
                }).done(function (results) {
                    employees = results;
                });

                return employees;
            } else {
                return [];
            }
        }
    }
});

I know this isn't working because I'm returning before the call is complete. I've seen how to use deferredobjects and what not but I can't seem to figure out how to implement it with Vue.

2
  • 1
    github.com/foxbenjaminfox/vue-async-computed might be useful. Commented Jun 13, 2018 at 16:35
  • 1
    @DanielBeck I have decided to not use a computed property for what I am trying to do. But thank you for that link. I'll definitely check it out and maybe use it in a future project. Commented Jun 13, 2018 at 18:25

4 Answers 4

2

This is what vue-async-computed is meant for. It resolves the promise you returned and handles any race conditions.

new Vue({
    el: "#formCompleteContainer",
    data: {
        form: {},
        components: []
    },
    asyncComputed: {
        employeeList: function () {
            if (_.some(this.components, function (component) {
                return component.ComponentInfo.Type === 8
            })) {
                return $.ajax({
                    url: "/Form/GetAllUsers",
                    type: "GET"
                });
            } else {
                return Promise.resolve([]);
            }
        }
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

A 3rd party implementation is far from needed to make this work.
1

For your use case, I don't think computed property can implement the goal.

My solution:

  1. create one data property as one 'defered' object,
  2. then uses one watch to async call your backend to get new data, finally assign to the defered object

like below demo:

Vue.config.productionTip = false
app = new Vue({
  el: "#app",
  data: {
    product: "Boots",
    deferedProduct: ''
  },
  watch: {
    product: function (newVal, oldVal) {
      setTimeout(() => {
        this.deferedProduct = 'Cats in ' + newVal + '!'
      }, 1500)
    }
  },
  methods: {
    nextProduct: function () {
      this.product += 'a'
    }
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <button @click="nextProduct()">Click Me!</button>
    <h2>{{product}}</h2>
    <h2>{{deferedProduct}}</h2>
</div>

Comments

1

After doing some more research I have gone another route. I agree with Sphinx that I don't think what I am trying to achieve will work with a computed property.

Instead, this is what I am going with:

new Vue({
    el: "#formCompleteContainer",
    data: {
        form: {},
        components: [],
        employees: []
    },
    methods: {
        getEmployees: function () {
            var self = this;
            if (_.some(this.components, function (component) {
               return component.ComponentInfo.Type === 8;
            })) {
                $.ajax({
                    url: "/Form/Form/GetAllUsers",
                    type: "GET"
                }).done(function (results) {
                    self.employees = results;
                });
            }
        }
    },
    created: function () {
        this.form = pageModel.Form;
        this.components = pageModel.Components;
    },
    mounted: function () {
        this.getEmployees();
    }
});

Comments

0

As pointed out already, mounted and other 3rd party solutions can work.

However, better readability and component loading will come from putting the desired Promise within a data property. And then using the Vue lifecycle hook created, we can wait for that Promise to resolve with a .then.

For example:

requestService.js:

...
   async foo(){
      let myRequest = someRequest.createInstance()
      await myRequest.onReady()
      return myRequest.getSomePromise()
   } 
...

And then import the service into your component, as well as declaring a data prop:

myComponent.vue

...
   data: (){
      myPromiseLoc: null,
   }
...
   created: (){
      requestService.foo().then( result =>
      {
         this.myPromiseLoc = result
      }
   }
...

2 Comments

This will only do the request once when the component is loaded. The task was to load the data only if a component with type 8 is displayed.
@Michael2 The SPECIFIC use case is always different. This questions, and the actual problem, has to do with async/await, promises, and lifecycle hooks. This answer encapsulates the users actual question just fine.

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.