Thursday, November 19, 2015

Angular - Handling promise status using filter

In angular application you can often see a code logically similar to:
function SubmitController($http) {
  var vm = this;

  vm.inProgress= false;
  vm.resolved = false;
  vm.rejected = false;

  vm.submitData = function() {
    vm.inProgress = true;
    return $http.post('....')
      .then(function() {
        vm.resolved = true;
        //Doing something usefull
      })
      .catch(function() {
        vm.rejected = true;
      })
      .finally(function() {
        vm.inProgress = false;
      });
  }
}
And a template
<button ng-disabled='vm.inProgress' ng-click='vm.submitData()'>Submit</button>
<span class='ng-hide' ng-show='vm.inProgress'>Submitting...</span>
<span class='ng-hide' ng-show='vm.resolved'>Submitted succsssfully!</span>
<span class='ng-hide' ng-show='vm.rejected'>Failed to submit!</span>
Looks familiar, isn't it? And it's getting even more messy if you have several actions like this. So following the DRY principal I came across with a very simple and flexible solution, just keep reading :)

The Filter

The filter is applied on the result of the action that returns promise and transforms it to a status object. The object has inProgress, resolved and rejected properties. The filter will be updating those properties on resolve or reject cases. So here is the code:
function promiseStatus() {
  return function(promise) {
    var status = {
      inProgress: true,
      resolved: false,
      rejected: false
    };
    promise
      .then(function() {
        status.resolved = true;
      })
      .catch(function() {
        status.rejected = true;
      })
      .finally(function() {
        status.inProgress = false;
      });
    return status;
  }
}

All you need to do is adjust your template like this
<button 
  ng-disabled='status.inProgress' 
  ng-click='status = (vm.submitData() | promiseStatus)'>Submit</button>
<span class='ng-hide' ng-show='status.inProgress'>Submitting...</span>
<span class='ng-hide' ng-show='status.resolved'>Submitted succsssfully!</span>
<span class='ng-hide' ng-show='status.rejected'>Failed to submit!</span>
As you can see the template is pretty match the same but your controller is crystal clear now:
function SubmitController($http) {
  var vm = this;

  vm.submitData = function() {
    return $http.post('....')
      .then(function() {
        //Doing something usefull
      });
  }
}

Full source code can be found on Github.

No comments:

Post a Comment