4

I'm afraid I may have gone down the rabbit hole of recursive promises.

I have a service that handles my api. (It's got an extra layer of promise so that I could switch back to a local json if the api went offline. (Not sure how necessary it is anymore) - mayte I should eliminate it for simplicity). Then I've got the promised async call in my controller.

This all works great as long as I get the data I expect, but it doesn't handle errors very well. When I get 400's and 500's, it doesn't send the error message to the user via toastr.

Sadly, this is not a fully-compliant RESTful api. The 400 error I get back is simply

{"Message":"No packages found"}

I don't really get how to get this to behave as it should, and replace success/error with then/catch (as per Angular best practice).

Here is a typical service call:

var _getPackagesPage = function (options) {

    var pageSize = options.data.pageSize;
    var page = options.data.page -1;
    return $q (function(resolve, reject) {
        switch (dataSource) {
            case 'api'://staging - live api data
                return $http({
                    method: 'get',
                    url: serviceBase + 'api/Packages?pageSize=' + pageSize + '&page=' + page
                }).then(function(results) {
                    resolve(results);
                });
                break;

            default: // dev - local json

                $.getJSON('Content/data/Packages.json', function (json) {
                    var pageSize = options.data.pageSize;
                    var page = options.data.page;
                    var newjson = json.splice(page*pageSize,pageSize);
                    resolve(newjson);
                });
        }
    });
};

and a typical call in a controller:

(options is the data object handed back to my data grid (Kendo))

    vm.getPackages = function(options) {
        return packagesService.getPackagesPage (options)
            .then(function(results) {
                options.success(results.data.Items);
            })
            .catch(function(error) {
                options.error(error);
                toastr.error(error.Message);
            });
    };

How can I clean this up?

[ UPDATE ] Attempted fix per Answer 1, below

Service:

    var _getOrdersPage = function (options) {

        var deff = $q.defer();
        var pageSize = options.data.pageSize;
        var page = options.data.page -1;
        return $http({
            method: 'get',
            url: serviceBase + 'api/Packages?pageSize=' + pageSize + '&page=' + page
        })
        .then(
            function(results) {
                deff.resolve(results);
            },
            function(ex){ 
                deff.reject(ex);
            });
        return deff.promise;
    };

Controller:

    vm.getOrders = function (options) {
        return ordersService.getOrdersPage (options)
            .then(function(results) {
                console.log("results!");
                console.log(results);
            })
            .catch(function(error) {
                console.log("error!");
                console.log(error);
            });
    };

results in:

GET http://< myURL >/api/Packages?pageSize=20&page=0 400 (Bad Request)

results!

undefined

DaveC426913
  • 2,012
  • 6
  • 35
  • 63

2 Answers2

1

I'm removing the switch case for brevity.

var _getPackagesPage = function (options) {

var pageSize = options.data.pageSize;
var page = options.data.page -1;
var deff = $q.defer();

$http({
      method: 'get',
      url: serviceBase + 'api/Packages?pageSize=' + pageSize + '&page=' + page
      }).then(
           function(results) {
              deff.resolve(results);
            },
            function(ex){ 
              deff.reject(ex);
             });

 return deff.promise;
    };

Controller

vm.getOrders = function (options) {
    return ordersService.getOrdersPage (options)
        .then(
         function(results) {
            console.log("results!");
            console.log(results);
        },
        function(error) {
            console.log("error!");
            console.log(error);
        });
};

If you dont have any logic inside your service, then you could return the $http itself as $http inturn is a promise:

var _getPackagesPage = function (options) {

  var pageSize = options.data.pageSize;
  var page = options.data.page -1;

   return $http({
        method: 'get',
         url: serviceBase + 'api/Packages?pageSize=' + pageSize + '&page=' + page
      });

    };
Developer
  • 6,240
  • 3
  • 18
  • 24
  • This seems to return a success to my controller, even though it's a 400. So options.success(results.data.Items) returns data = undefined. – DaveC426913 Aug 28 '16 at 18:25
  • When your service returns 400, that would come to the error call back of .then method from where we reject the promise. This will not reach your success call back in controller, this will hit the error callbacj instead. – Developer Aug 28 '16 at 18:35
  • And yet that is not what happens. I've added the attempted fix and the outcome, to the opening post. – DaveC426913 Aug 28 '16 at 18:47
  • Could you verify whether its hitting deff.reject(ex); in the service? – Developer Aug 29 '16 at 01:33
  • It is indeed hitting deff.reject. ex contains Status: 400, StatusMessage: "Bad Request". – DaveC426913 Aug 29 '16 at 13:57
  • Ah. That seems to do the trick. Now I find a message under error.StatusText – DaveC426913 Aug 29 '16 at 15:34
  • No. I spoke too soon. It is actually captured in .then(), not in catch(). – DaveC426913 Aug 29 '16 at 15:44
  • @DaveC426913 - not sure why you specifically need that in catch block; that's what error callback is for. If you need this in catch block, then try throwing error instead of deff.reject(ex); – Developer Aug 29 '16 at 16:48
  • @DaveC426913 - check this fiddle: https://plnkr.co/edit/DuGLKhRmpnJH7yJzxpHQ?p=preview . Im getting the exception in catch block. – Developer Aug 30 '16 at 12:23
0

You have too many returns in your service. The second one is not called.

You don't need to create a promise manually since $http returns apromise.

You're not returning data from your service.

var _getOrdersPage = function(options) {
        var pageSize = options.data.pageSize;
        var page = options.data.page -1;
        return $http({
            method: 'get',
            url: serviceBase + 'api/Packages?pageSize=' + pageSize + '&page=' + page
        })
        .then(
            function(results) {
                return results;
            },
            function(ex){
                return ex;
            });
}

Your controller is fine, you can use catch() or pass an error callback.

Example:

function myService($http) {
  this.getData = function(url) {
    return $http.get(url).
    then(function(response) {
      return response.data;
    }, function(error) {
      return error;
    });
  }
};

function MyController(myService) {
  var vm = this;  
  vm.result = [];
  vm.apiUrl = "https://randomuser.me/api/";
  myService.getData(vm.apiUrl).then(function (data) {
    vm.result = data;
  },
  function(error) {
    console.log(error);
  });
};

angular.module('myApp', []);
angular
    .module('myApp')
    .service('myService', myService)
    .controller('MyController', MyController);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="MyController as ctrl">
    {{ ctrl.result }}
  </div>
</div>
gyc
  • 4,300
  • 5
  • 32
  • 54