43

I am using angular-ui-router's resolve to get data from server before moving to a state. Sometimes the request to the server fails and I need to inform the user about the failure. If I call the server from the controller, I can put then and call my notification service in it in case the call fails. I put the call to the server in resolve because I want descendant states to wait for the result from the server before they start.

Where can I catch the error in case the call to the server fails? (I have read the documentation but still unsure how. Also, I'm looking for a reason to try out this new snippet tool :).

"use strict";

angular.module('MyApp', ["ui.router"]).config([
  "$stateProvider",
  "$urlRouterProvider",
  function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise("/item");
    $stateProvider
    .state("list", {
      url: "/item",
      template: '<div>{{listvm}}</div>' +
      	'<a ui-sref="list.detail({id:8})">go to child state and trigger resolve</a>' +
        '<ui-view />',
      controller: ["$scope", "$state", function($scope, $state){
          $scope.listvm = { state: $state.current.name };
      }]
    })
    .state("list.detail", {
      url: "/{id}",
      template: '<div>{{detailvm}}</div>',
      resolve: {
        data: ["$q", "$timeout", function ($q, $timeout) {
          var deferred = $q.defer();
          $timeout(function () {
            //deferred.resolve("successful");
            deferred.reject("fail");   // resolve fails here
          }, 2000);
          return deferred.promise;
        }]
      },
      controller: ["$scope", "data", "$state", function ($scope, data, $state) {
        $scope.detailvm = {
          state: $state.current.name,
          data: data
        };
      }]
    });
  }
]);
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.min.js"></script>

<div ng-app="MyApp">
  <ui-view />
</div>

2 Answers 2

81

Old question but I had the same problem and stumbled on this in ui-router's FAQ section

If you are having issues where a trivial error wasn't being caught because it was happening within the resolve function of a state, this is actually the intended behavior of promises per the spec.

errors within resolve.

So you can catch all resolve errors in the run phase of your app like this

$rootScope.$on('$stateChangeError', 
function(event, toState, toParams, fromState, fromParams, error){ 
        // this is required if you want to prevent the $UrlRouter reverting the URL to the previous valid location
        event.preventDefault();
        ... 
})
Sign up to request clarification or add additional context in comments.

1 Comment

This should be the accepted answer. imho the whole point of using resolve is to not have to deal with data in the controller!
22

The issue is that if any of the dependencies in the route resolve is rejected, the controller will not be instantiated. So you could convert the failure to data that you can detect in the instantiated controller.

Example Pseudocode:-

   data: ["$q", "$timeout","$http", function ($q, $timeout, $http) {
      return $timeout(function () { //timeout already returns a promise
        //return "Yes";
        //return success of failure
         return success ? {status:true, data:data} : {status:false}; //return a status from here
       }, 2000);
     }]

and in your controller:-

 controller: ["$scope", "data", "$state", function ($scope, data, $state) {
      //If it has failed
      if(!data.status){
        $scope.error = "Some error";
       return;
      }
        $scope.detailvm = {
          state: $state.current.name,
          data: data
        };

If you are making an $http call or similar you can make use of http promise to resolve the data always even in case of failure and return a status to the controller.

Example:-

resolve: {
        data: ["$q", "$timeout","$http", function ($q, $timeout, $http) {
           return $http.get("someurl")
             .then(function(){ return {status:true , data: "Yes"} }, 
                    function(){ return {status:false} }); //In case of failure catch it and return a valid data inorder for the controller to get instantated
        }]
      },

"use strict";

angular.module('MyApp', ["ui.router"]).config([
  "$stateProvider",
  "$urlRouterProvider",
  function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise("/item");
    $stateProvider
    .state("list", {
      url: "/item",
      template: '<div>{{error}}</div><div>{{listvm}}</div>' +
      	'<a ui-sref="list.detail({id:8})">go to child state and trigger resolve</a>' +
        '<ui-view />',
      controller: ["$scope", "$state", function($scope, $state){
       $scope.listvm = { state: $state.current.name };
      }]
    })
    .state("list.detail", {
      url: "/{id}",
      template: '<div>{{detailvm}}</div>',
      resolve: {
        data: ["$q", "$timeout","$http", function ($q, $timeout, $http) {
           return $http.get("/").then(function(){ return {status:true , data: "Yes"} }, function(){ return {status:false} })
        }]
      },
      controller: ["$scope", "data", "$state", function ($scope, data, $state) {
   
    
        $scope.detailvm = {
          state: $state.current.name,
          data: data.status ? data :"OOPS Error"
        };
        
      }]
    });
  }
]);
 <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
    <script data-require="angular-ui-router@*" data-semver="0.2.10" src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
  <div ng-app="MyApp">
      <ui-view></ui-view>
    </div>

1 Comment

This way you have to implement error handling in each controller, right? I like better the approach where you listen for error event that is broadcasted by ui router and respond to that, since error handling is centralized in that case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.