Return a promise in a computed properties in a controller


#1

Hi,

I have troubles getting a value in a computed properties in a controller. It seems to me that a computed property doesn’t work well with a promise. But I’m not sure whether I do something stupidly wrong. Here is the computed property in a controller

    userNickname: function() {        
       return this.store.find('user', this.model.get('user')).then(function(user) {
           Ember.debug('****user nickname: ' + user.get('nickname'));
              return user.get('nickname');
        });
    }.property('model'),

And in the template I use

  {{userNickname}}

The promise returns correct value. I see from the console that the right nickname is logged. But the page only displays [object Object]

Am I missing anything here? What’s the best way to load piece of data only when it’s needed?


#2

From first glance it looks like the use of two returns would be your problem.

return this.store.find('user', this.model.get('user')).then(function(user) {

When this promise is returned, the nested item has not yet resolved. Does that make sense?


#3

Well. Is the promise isn’t returned, there’s nothing showing in the template, not even [object Object].

The nested item runs and the nickname is correct not matter whether the promise is returned. But the value isn’t showing in the template.

If a fixed value is returned instead of a promise, that value is displayed correctly.


From: Martin Feckie

Sent: ‎11/‎24/‎2014 12:58 PM

To: hellomao@outlook.com

Subject: [Ember.JS] Return a promise in a computed properties in a controller

mfeckie

November 24

From first glance it looks like the use of two returns would be your problem.

return this.store.find('user', this.model.get('user')).then(function(user) {

When this promise is returned, the nested item has not yet resolved. Does that make sense?

To respond, reply to this email or visit http://discuss.emberjs.com/t/return-a-promise-in-a-computed-properties-in-a-controller/6783/2 in your browser.


Previous Replies

kceiw

November 24

Hi,

I have troubles getting a value in a computed properties in a controller. It seems to me that a computed property doesn't work well with a promise. But I'm not sure whether I do something stupidly wrong. Here is the computed property in a controller

    userNickname: function() {
       return this.store.find('user', this.model.get('user')).then(function(user) {
           Ember.debug('****user nickname: ' + user.get('nickname'));
              return user.get('nickname');
        });
    }.property('model'),

And in the template I use

  {{userNickname}}

The promise returns correct value. I see from the console that the right nickname is logged. But the page only displays [object Object]

Am I missing anything here? What's the best way to load piece of data only when it's needed?


To respond, reply to this email or visit http://discuss.emberjs.com/t/return-a-promise-in-a-computed-properties-in-a-controller/6783/2 in your browser.

To unsubscribe from these emails, visit your user preferences.


#4

Sorry, the promise is an object. That’s why it’s being displayed in that manner. When the promise resolves, it is the data …

For example ->

return this.store will return a promise object. What you are trying to access is the userNickname, which is not available at the time when your userNickname function returns. You have two returns inside your function, you need to have one. Hope that makes sense


#5

What you said makes sense. But that doesn’t solve the problem. I think I have a typo in my previous reply. If the promise isn’t returned, it’s

userNickname: function() {
   this.store.find('user', this.model.get('user')).then(function(user) {
       Ember.debug('****user nickname: ' + user.get('nickname'));
          return user.get('nickname');
    });
}.property('model'),

Then

   {{userNickname}}

will be empty.

I think the question becomes, how to use a promise in a computed property. Or is it possible?


#6

Probably this scenario isn’t supported. I find a workaround here


#7

I think that you should use the PromiseMixin , in that case you would return a ObjectPromiseController (as per in official example) for user in your promise, and use ObjectPromiseController.nickname in your template.

If you are using EmberData you can also use DS.PromiseObject which is essentially the same as ObjectPromiseController but build upon ObjectProxy instead.


#8

Wouldn’t it be easier just to make usernickname a stand alone property on the controller, and use .observe(‘model’) to kickstart the store.find and update the property when the promise returns?


#9

you can use (coffeescript)

userNickname: (()->
  @store.find("user", @model.get("user")).then (user) =>
     nickname = user.get "nickname"
     @set "userNickname", nickname
  ''
).property("model")

#10

Thank you all for the reply. I’ll try out those options. The one in the link in my previous reply works too.


#11

I have had similar problems in the past and after figuring out how to bash the Controller into doing what I needed I realised that the happy path for Ember is to deal with promises in the Route because he *model hooks in the route automatically wait for async data. A ugly kluge in the Controller became a neat one-liner in the Route. As I thought about it more it became obvious that the fetching of data was really a routing layer concern (at least in Ember’s dialect of routing).

If you are not already familiar with RSVP.hash and friends allow you to return multiple models from the Route model hook.

I realise that isn’t actually an answer to your question (sorry) and you may have constraints that prevent your from handling async in the Route layer but I hope this is still somewhat helpful.


#12

I have a similar need. The issue is this and I would love feedback as to the best pattern:

Many of the model relationships are async. So I’m returning promises essentially everywhere. I get that the data fetching should happen at the route level, but how about async calls that are initiated by the model.

The issue is I need to access data several relationship across:

this.get('posts') then posts.get('comments') then comments.get('tags')
Then calculate on those tags and return the value

I could set the properties I need on init, but I would much rather have these lazily computed.

Wish there was a simple solution to have these computed props behave like the model hook in a route.