0
votes

First: I know it is not the desired way to have promises in a computed property. However in my setup it fits best. I currently use Ember 1.8.0-beta.1 for compatibility purposes.

What I am trying to accomplish is:

  • get all weeknummers including the desired color
  • make links of them

In my controller:

weeknumbersChoicesWithColor: function () {
    var weeknumberChoices = this.get('weeknumbersChoices');
    var yearnumber = this.get('yearnumber');
    var self = this;

    var promises = [];
    weeknumberChoices.forEach(function (weeknumber) {
        var promise = self.store.findQuery('order', {'weeknumber': weeknumber, 'yearnumber': yearnumber}).then(function(orders){
            return orders.set('weeknumber', weeknumber);
        });
        promises.push(promise);
    });

    return Ember.RSVP.all(promises).then(function(result){

        result.forEach(function(weekOrders){
            // ... do something to get weeknumer and color

            return {
                weeknumber: weeknumber,
                color: color
            };
        });
    });
}.property('yearnumber', 'weeknumber'),

In my template:

{{#each weeknumbersChoicesWithColor}}
     <li>{{#link-to 'orders' (query-params weeknumber=this.weeknumber)}}{{this.weeknumber}}{{/link-to}}</li>
{{/each}}

My template however trows this error: Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed {_id: 131, _label: undefined, _state: undefined, _result: undefined, _subscribers: } This is of course because the promise is not fullfilled yet. When I wrap them in a {{#if weeknumbersChoicesWithColor.isFulfilled}} block it also does not work too.

It does work when I do not return a promise, but set a property in the controller with the array, with an Ember.run.later however that seems to much hacking to me and will not always work.

2

2 Answers

1
votes

Specifically for that purposes Ember has PromiseProxies. Here you can read how to wrap single object into proxy for proper usage in template with

{{#if myPromiseProxy.isFulfilled}}
  // do stuff
{{/if}}

and here is an ArrayProxy which you actually need.

Here is some relevant code:

weeknumbersChoicesWithColor: function () {
    var ArrayPromiseProxy = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin);
    var weeknumberChoices = this.get('weeknumbersChoices');
    var yearnumber = this.get('yearnumber');
    var self = this;

    var promises = [];
    weeknumberChoices.forEach(function (weeknumber) {
        var promise = self.store.findQuery('order', {'weeknumber': weeknumber, 'yearnumber': yearnumber}).then(function(orders){
            return orders.set('weeknumber', weeknumber);
        });
        promises.push(promise);
    });

    promises = Ember.RSVP.all(promises).then(function(result){

        result.forEach(function(weekOrders){
            // ... do something to get weeknumer and color

            return {
                weeknumber: weeknumber,
                color: color
            };
        });
    });
    return ArrayPromiseProxy.create({
        promise: promises
    });
}.property('yearnumber', 'weeknumber'),

Then in template:

{{#if weeknumbersChoicesWithColor.isFulfilled}}
    {{#each weeknumbersChoicesWithColor}}
        <li>{{#link-to 'orders' (query-params weeknumber=this.weeknumber)}}{{this.weeknumber}}{{/link-to}}</li>
    {{/each}}
{{/if}}

Ember actually has ArrayPromiseProxy implementation that you can use so you don't have to extend ArrayProxy with PromiseProxyMixin every time which is a DS.PromiseArray. But as its primary use case is dealing with model data it is kept under ember-data namespace and i generally just use my own version as not all projects would use Ember Data.

To clear your thoughts on why this error happens in the first place i must add that RSVP.all(someArrayOfPromises) is an object and not an array. This object would set its result property to the value of an array when it resolves but it shouldn't be considered as an array on its own. The proxies are there to wrap the actual result of RSVP object execution and add convenience properties to check execution state and respond with respective display logic depending on the current promise state. ArrayProxy would also map some of the Array methods to its content property which is null at the moment of instantiation and becomes an actual array when RSVP.all resolves.

1
votes

You need to use map function.

return result.map(function(weekOrders){
            // ... do something to get weeknumer and color

            return {
                weeknumber: weeknumber,
                color: color
            };
        });