AngularJS - отклонил $http обещание с помощью $routeProvider: разрешить - программирование
Подтвердить что ты не робот

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 и разрешать его, даже если запрос является провалом, но мне интересно, есть ли более простое решение.

Спасибо за чтение и, надеюсь, помощь:)

4b9b3361

Ответ 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');
        });
    }
};

Не очень умный, но работает как намеренный... Если у кого-то еще есть решение исходной проблемы, я был бы очень рад прочитать его!