sendAction as a promise

I’m working on a toggle button component, that needs to know if the action was completed or not, so it can @set 'isSending', false.

Easy way, is to just reveal the binding outside, and whoever handles the action handles setting isSending to false.

{{toggle-btn
    text="Like"
    selectedText="Liked"
    icon="fa fa-heart"
    toggleState=post.isVoted
    isSending=isVoting
    action="vote"
    param=post
}}

But it doesn’t make sense to be handled in the action, because it is related to the component.

So i was thinking maybe promises can solve this problem. Maybe something like this

new Ember.RSVP.Promise( ->
  self.sendAction('action', self.get('param'))
).then ->
  console.log 'done'

Any idea if this is possible, or does it require ember support?

Hey @seif, the short answer is that this doesn’t work this way in Ember. As I understand it, Ember’s action handlers only ever return true or false, and these boolean return values are used to determine whether handling the action “bubbles” to the next target up the chain. Even though actions sent from a component using sendAction do not bubble, they use the same action-handling code that the rest of ember uses, which coerces the return value to true or false.

Ember’s implementation of sendAction internally uses triggerAction. sendAction doesn’t actually return the result of triggerAction, but even if it did it would only be a boolean, as triggerAction only returns booleans.

I think the best way to do what you want to do here is to bind a property in the component to a property in its controlling context that changes asynchronously. If you needed to do some cleanup inside the component you could add an observer to that property, too.

Here’s a jsbin showing how you might do this by observing a property on your controller:

http://jsbin.com/AJeqEki/3/edit

So I still have to set isSending from outside of the component. I guess there is no other way around it

@seif I’ve been thinking about something very similar lately. I spoke to @machty about it and he pointed me to a computed property that he’s been using.

// A PromiseAction is an action set on the controller,
// but not in the actions hash, which allows it to be
// passed in directly to the component. The component
// just sees it as a function that it can call that
// returns a promise. This is nice for when the component
// needs to emit some action that can fail; if it fails
// it can clean up after itself.
 
// Example:
// export default Controller.extend({
//   setSomeValueAsyncly: promiseAction(function(newValue) {
//     this.set('something', newValue);
//     // simulate a slow set...
//     return new Ember.RSVP.Promise(function(resolve) {
//       Ember.run.later(resolve, 1000);
//     });
//   })
// });
//
// {{some-component on-change=setSomeValueAsyncly}}
 
export default function promiseActionComputedProperty(fn) {
  return Ember.computed(function() {
    var self = this;
    return function() {
      var args = arguments;
      return new Ember.RSVP.Promise(function(resolve) {
        resolve(fn.apply(self, args));
      });
    };
  });
};

I’m looking at how I can use it myself but I just wanted to share.

2 Likes

@tarasm That is awesome, thanks a lot for sharing :smile: