declaring a promise in angularJS with named success/error callbacks

All we need is an easy explanation of the problem, so here it is.

I am trying to do something very similar to the $http service. From my understanding $http return a promise object.

When using it the syntax is :

$http(...).success(function(data)) {
   //success callback
}).error(function(data)) {
   //error callback
})

I would like to do just the same but consider my API is GetUserProfile, so I wish to have the syntax :

GetUserProfile(...).success(function(data) {
   // success callback
}).error(function(data)) {
   // error callback
})

how can I accomplish that using a promise ?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

The nice thing with open-source is that you can read the source. Here’s how the $http service does it:

  promise.success = function(fn) {
    promise.then(function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  promise.error = function(fn) {
    promise.then(null, function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

Method 2

You need to use the $q service and create and return your own promise in GetUserProfile:

function GetUserProfile() {
    var deferred = $q.defer();
    var promise = deferred.promise;

    // success condition
    if (!true) {
        deferred.resolve('data');
    // error condition
    } else {
        deferred.reject('error');
    }

    promise.success = function(fn) {
        promise.then(fn);
        return promise;
    }

    promise.error = function(fn) {
        promise.then(null, fn);
        return promise;
    }

    return promise;
}

GetUserProfile()
    .success(function(data) {
        console.log(data);
    })
    .error(function(error) {
        console.error(error);
    });

Method 3

You don’t need change source code.
Angular provide a way to change any service in angular include $q.

$provide.decorator is perfect for your requirement
here is my code.

put it at app.module(‘…’).config

$provide.decorator('$q', function($delegate) {
  function httpResponseWrapper(fn) {
    return function(res) {
      if (res.hasOwnProperty('data') && res.hasOwnProperty('status') && res.hasOwnProperty('headers') && res.hasOwnProperty('config') && res.hasOwnProperty('statusText')) {
        return fn(res.data, res.status, res.headers, res.config, res.statusText);
      } else {
        return fn(res);
      }
    };
  };
  function decorator(promise) {
    promise.success = function(fn) {
      return decorator(promise.then(httpResponseWrapper(fn)));
    };
    promise.error = function(fn) {
      return decorator(promise.then(null, httpResponseWrapper(fn)));
    };
    return promise;
  };
  var defer = $delegate.defer;
  $delegate.defer = function() {
    var deferred = defer();
    decorator(deferred.promise);
    return deferred;
  };
  return $delegate;
});

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply