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 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With