AngularJS - отклонил $http обещание с помощью $routeProvider: разрешить
Я пытаюсь использовать resolve
в $routeProvider
для отображения нового маршрута только после завершения запроса $http
. Если запрос выполнен успешно, обещание, полученное в результате $http.post(), разрешено, и представление отображается. Но если запрос терпит неудачу (тайм-аут или внутренняя ошибка, например,), обещание никогда не разрешается, и представление никогда не отображается. Как я могу справиться с отказом запроса с помощью resolve
?
Наиболее важные части кода ниже:
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;
}
};
} ]);
Я знаю, что могу использовать обещание вокруг вызова $http и разрешать его, даже если запрос является провалом, но мне интересно, есть ли более простое решение.
Спасибо за чтение и, надеюсь, помощь:)
Ответы
Ответ 1
Я думаю, что единственный способ сделать это из resolve
- это вручную разрешить обещание, возвращенное Warranty.sendRequest
, и переустановить его в новом обещании:
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;
} ]
}
В WarrantyResultCtrl
вы можете проверить, произошла ли ошибка и сгенерировать перенаправление.
ИЗМЕНИТЬ: гораздо более чистое решение:
// WarrantyCtrl
$scope.$on('$routeChangeError', function() {
// handle the error
});
$scope.submitWarranty = function() {
$scope.loading = true;
Warranty.setRequestData($scope.data);
$location.path('/warrantyResult');
};
(демонстрация plunker)
Ответ 2
Я обнаружил, что контроллер не запускается вообще, если Promise
есть rejected
, и ваше представление никогда не отображается - то же, что и вы.
Я также обнаружил, что если вы обрабатываете Promise
с .then()
в $routeProvider
resolve
, то .then()
вернет новый Promise
, который resolved
, и ваш контроллер будет в конце концов, хотя и без данных, которые вы ожидаете.
Например:
$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?
});
}]
}
});
Теперь в вашем контроллере вы захотите проверить, есть ли response
undefined
. Если это undefined
, то вы узнаете, что вызов Warranty.sendRequest()
завершился неудачно, и вы можете действовать соответственно.
Для чего это стоит, я не пошел по этому пути. Вместо этого я ввел службу $location
в обработчик resolve
и перенаправлен на страницу с ошибкой, если вызов $http
отклоняется.
UPDATE
Просто заметили, что в вашем контроллере вы вводите службу Warranty
, когда вместо этого вы должны вводить response
, который вы определили в своем resolve
. Это предотвратит рендеринг вашего представления до тех пор, пока не будет разрешено обещание, возвращенное из Warranty.sendRequest()
.
Ответ 3
После глубокого поиска я не смог найти решение этой проблемы.
Я решил удалить аргумент разрешения в определении маршрута, и я использую следующий фрагмент кода в 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');
});
}
};
Не очень умный, но работает как намеренный... Если у кого-то еще есть решение исходной проблемы, я был бы очень рад прочитать его!