最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS - rejected $http promise with $routeProvider:resolve - Stack Overflow

programmeradmin7浏览0评论

I am trying to use resolve in a $routeProvider to display the new route only when a $http request is finished. If the request is successful, the promise resulting from the $http.post() is resolved and the view is rendered. But if the request fails (timeout or internal error for eg.), the promise is never resolved and the the view is never rendered. How can I deal with request failure using resolve ?

The most important parts of the code is bellow :

app.js

$routeProvider.when('/warrantyResult', {
    templateUrl : 'partials/warranty-result.html',
    controller : 'WarrantyResultCtrl',
    resolve : {
        response : [ 'Warranty', function(Warranty) {
            return Warranty.sendRequest();
        } ]
    }
});

controllers.js

angular.module('adocDemo.controllers', []).controller('HomeCtrl', [ '$scope', function($scope) {

} ]).controller('WarrantyCtrl', [ '$scope', '$http', '$location', 'Warranty', function($scope, $http, $location, Warranty) {
    $scope.submitWarranty = function() {
        $scope.loading = true;
        Warranty.setRequestData($scope.data);
        $location.path('/warrantyResult');
    };
} ]).controller('WarrantyResultCtrl', [ '$scope', 'Warranty', function($scope, Warranty) {

    $scope.request = Warranty.getRequestData();
    $scope.response = Warranty.getResponseData();
} ]);

services.js

angular.module('adocDemo.services', []).factory('Warranty', [ '$http', '$timeout', function($http, $timeout) {
    /**
     * This service is used to query the Warranty Webmethod. The sendRequest
     * method is automaticcaly called when the user is redirected to
     * /warrantyResult route.
     */
    var isDataSet = false;
    var requestData = undefined;
    var responseData = undefined;
    return {
        setRequestData : function(data) {
            //Setting the data
            isDataSet = true;
        },
        getRequestData : function() {
            return requestData;
        },
        sendRequest : function(data) {
            if(isDataSet) {
                var request = $http.post('url/to/webservice', requestData);
                request.success(function(data) {
                    responseData = data;
                });
                return request;
            }   
        },
        getResponseData : function() {
            return responseData;
        }
    };
} ]);

I know i could use a promise around the $http call and resolve it even if the request is a failure, but I'm wondering if there is a simpler solution.

Thanks for reading and, hopefully, helping :)

I am trying to use resolve in a $routeProvider to display the new route only when a $http request is finished. If the request is successful, the promise resulting from the $http.post() is resolved and the view is rendered. But if the request fails (timeout or internal error for eg.), the promise is never resolved and the the view is never rendered. How can I deal with request failure using resolve ?

The most important parts of the code is bellow :

app.js

$routeProvider.when('/warrantyResult', {
    templateUrl : 'partials/warranty-result.html',
    controller : 'WarrantyResultCtrl',
    resolve : {
        response : [ 'Warranty', function(Warranty) {
            return Warranty.sendRequest();
        } ]
    }
});

controllers.js

angular.module('adocDemo.controllers', []).controller('HomeCtrl', [ '$scope', function($scope) {

} ]).controller('WarrantyCtrl', [ '$scope', '$http', '$location', 'Warranty', function($scope, $http, $location, Warranty) {
    $scope.submitWarranty = function() {
        $scope.loading = true;
        Warranty.setRequestData($scope.data);
        $location.path('/warrantyResult');
    };
} ]).controller('WarrantyResultCtrl', [ '$scope', 'Warranty', function($scope, Warranty) {

    $scope.request = Warranty.getRequestData();
    $scope.response = Warranty.getResponseData();
} ]);

services.js

angular.module('adocDemo.services', []).factory('Warranty', [ '$http', '$timeout', function($http, $timeout) {
    /**
     * This service is used to query the Warranty Webmethod. The sendRequest
     * method is automaticcaly called when the user is redirected to
     * /warrantyResult route.
     */
    var isDataSet = false;
    var requestData = undefined;
    var responseData = undefined;
    return {
        setRequestData : function(data) {
            //Setting the data
            isDataSet = true;
        },
        getRequestData : function() {
            return requestData;
        },
        sendRequest : function(data) {
            if(isDataSet) {
                var request = $http.post('url/to/webservice', requestData);
                request.success(function(data) {
                    responseData = data;
                });
                return request;
            }   
        },
        getResponseData : function() {
            return responseData;
        }
    };
} ]);

I know i could use a promise around the $http call and resolve it even if the request is a failure, but I'm wondering if there is a simpler solution.

Thanks for reading and, hopefully, helping :)

Share Improve this question edited Jun 7, 2013 at 15:17 pdegand59 asked Jun 7, 2013 at 15:08 pdegand59pdegand59 13k9 gold badges54 silver badges58 bronze badges 2
  • Depends how you want your app to behave when resolve fails? – Stewie Commented Jun 7, 2013 at 15:26
  • The best behavior would be to redirect the user to a different route (/requestError ?) when the resolve fails. Or the /warrantyResult could be rendered and the controller of this route would be in charge to check if the request was a success or not. – pdegand59 Commented Jun 7, 2013 at 15:31
Add a ment  | 

3 Answers 3

Reset to default 8

I think the only way to do it from resolve is to manually resolve the promise returned by Warranty.sendRequest and rewrap it in a new promise:

resolve : {
  response : [ 'Warranty' '$q', function(Warranty, $q) {
    var dfd = $q.defer();

    Warranty.sendRequest().then(function(result) {
      dfd.resolve({ success: true, result : result });
    }, function(error) {
      dfd.resolve({ success : false, reason : error });
    });

    return dfd.promise;

  } ]
}

In WarrantyResultCtrl, you could check if an error occurred and generate a redirect.

EDIT: much cleaner solution:

// WarrantyCtrl
$scope.$on('$routeChangeError', function() {
  // handle the error
});
$scope.submitWarranty = function() {
    $scope.loading = true;
    Warranty.setRequestData($scope.data);
    $location.path('/warrantyResult');
};

(plunker demo)

What I have found is that the controller is not fired at all if the Promise is rejected, and your view is never rendered--same as you.

What I also discovered is that if you handle the Promise with a .then() in your $routeProvider's resolve, the .then() will return a new Promise that is resolved and your controller is fired after all, albeit without the data you are expecting.

For example:

$routeProvider.when('/warrantyResult', {
    templateUrl : 'partials/warranty-result.html',
    controller : 'WarrantyResultCtrl',
    resolve : {
        response : [ 'Warranty', function(Warranty) {
            return Warranty.sendRequest()
                .then(null, function(errorData) {
                    // Log an error here?
                    // Or do something with the error data?
                });
        }]
    }
});

Now in your controller you will want to check whether response is undefined. If it is undefined then you'll know that the call to Warranty.sendRequest() failed, and you can act accordingly.

For what it's worth, I did not go this route. Instead, I injected the $location service into the resolve handler and redirected to an error page if the $http call gets rejected.

UPDATE Just noticed that in your controller you are injecting the Warranty service when you should instead be injecting the response that you defined in your resolve. That will prevent your view from rendering until the Promise returned from Warranty.sendRequest() is resolved.

After deep searching, I could not find a solution for this problem.

I decided to drop the resolve statment in my route definition and I use the following snippet of code in the WarrantyCtrl.

$scope.submitWarranty = function() {
    $scope.formatUserData();
    if ($scope.verifyUserData()) {
        $scope.loading = true;
        Warranty.setRequestData($scope.data);
        Warranty.sendRequest().success(function() {
            $location.path('/warrantyResult');
        }).error(function() {
            $location.path('/webserviceError');
        });
    }
};

Not very clever, but works as intented ... If someone still have the solution for the original problem, I would be very pleased to read it !

发布评论

评论列表(0)

  1. 暂无评论