4

AngularJS 1.5.0 brings the $resolve property, designed to make it easier to pass the ngRoute resolve data into the view without the need to create a controller for directives. This property also works for regular views. So this works:

(function() {
  function ConfigureRoutes($routeProvider) {
    $routeProvider.
      when('/test', {
        templateUrl: 'test.html',
        controller: 'TestController',
        controllerAs: 'vm',
        resolve: {
          'stuff': ['$q', function($q) {
            return $q.when([
              { id: 1, name: 'John' },
              { id: 2, name: 'Jake' }
            ]);
          }]
        });
      }
      app.config(['$routeProvider', ConfigureRoutes]);
})();


<pre>{{ $resolve.stuff | json }}</pre>

However, there are times when I have several resolve functions I want to pass into the controller to mutate before using in the view. Currently I do something like:

when('/test', {
  ...
  resolve: {
    'stuff_1': ...
    'stuff_2': ...
  }
});

app.controller('StuffController', ['stuff_1', 'stuff_2', function(stuff_1, stuff_2) {
  this.stuff_1 = stuff_1;
  this.stuff_2 = stuff_2;
}]);

Is there a way of accessing this new $resolve property without having to access the parameters individually? I've tried:

app.controller('StuffController', ['$scope', function($scope) {
  this.stuff_1 = $scope.$resolve.stuff_1; // $scope.$resolve == undefined
}]);

app.controller('StuffController', ['$resolve', function($resolve) {
  this.stuff_1 = $resolve.stuff_1; // injector error
}]);

EDIT:

For those who may find this in the future, you can access this $resolve object a different way in your controller:

app.controller('TestController', function($route) {
  this.$resolve = $route.current.locals;
});

So the decision was made to not attach $resolve before the controller is instantiated.

The decision was also made not to made $resolve and injectable. This can be achieved using $q.all() as a single resolve function, like so:

function config($routeProvider) {
  $routeProvider.when('/',
    controller: 'TestController',
    controllerAs: 'vm',
    template: 'test.html',
    resolve: {
      $resolve: function($q) {
        return $q.all({
          local_1: $q.when(),
          local_2: $q.when()
        });
      }
    });
}

Or if you want to have a forked version, see the PRs at ($scope.$resolve before controller)[https://github.com/angular/angular.js/pull/14135]

($resolve as injectable) [https://github.com/angular/angular.js/pull/14136]

0

1 Answer 1

3

There is apparently a delay between when the controller is invoked and when the $resolve property is placed on scope.

angular.module("myApp").controller("test", function($scope, $timeout) {
    var vm = $scope;
    console.log("test controller")
    //console.log(vm);
    console.log("$resolve=",$scope.$resolve);  //Undefined
    $timeout(function() {
         console.log("timeout $resolve");
         console.log("$resolve=",$scope.$resolve); //Defined
    });
});

Using $timeout, the values resolve.

The DEMO on JSFiddle.


Example with $watch

angular.module("myApp").controller("test", function($scope) {
    var vm = $scope;
    console.log("test controller")
    console.log(vm);
    console.log("$resolve=", $scope.$resolve); //Undefined
    $scope.$watch("$resolve", function(value) {
         console.log("watch $resolve");
         console.log(value); //Defined
    });
});

Using $watch, the values resolve.

The DEMO on JSFiddle.


Review of Source Code

In ngView.js Line #273, the controller is instantiated.

In ngView.js Line #280, the $resolve property is put on scope.

This is a bug. There is no reason the $resolve property can't be put on scope before the controller is instantiated.


From GitHub:

feat(ngView): reference resolved locals in scope #13400

All the resolves for a route are now attached to the route's local scope, as the property whose name is given by the resolveAs property on the route definition.

If resolveAs is not specified it defaults to $resolve.

This will make it easier to use ngRoute, by being able to reference all the resolve values for the route, directly on the scope, rather than having to implement a controller just to copy the resolves across manually.

For example, rather than

$routeProvider.when('/', {
  resolve: {
    item1: ($http) => $http.get(...),
    item2: ($http) => $http.get(...)
  },
  template: '<my-app item1="vm.item1" item2="vm.item2"></my-app>',
  controllerAs: 'vm',
  controller: ['item1', 'item2', function(item1, item2) {
    this.item1 = item1;
    this.item2 = item2;
  }]
});

one can now do

$routeProvider.when('/', {
  resolve: {
    item1: ($http) => $http.get(...),
    item2: ($http) => $http.get(...)
  },
  template: '<my-app item1="$resolve.item1" item2="$resolve.item2"></my-app>'
});
Sign up to request clarification or add additional context in comments.

7 Comments

I knew there was something screwy here - the docs, as you show, point to using this for directives declared within the $routeProvider config, and I couldn't find any examples of using it in a typical controller-view scenario like I want. I wish I could add a few more upvotes for the work you did digging into the source. I glanced there but didn't dive in enough, I guess. I'll file a bug. It'd be nice if it was available to the injector as simply $resolve so I don't need to inject the whole $scope. It seems odd to set this.$resolve = $scope.$resolve when this becomes the scope...
It would be easy to add $resolve to the list of injected locals for the controller. It's a good idea. Include that suggestion when you file your new AngularJS Issue.
Sadly, see This fiddle - using $timeout the value will propagate to the console but still not to the view
See my example with $watch.
I submitted an issue and a pull request to fix the timing issue. I also have code ready that would allow $resolve or resolveAs to be injected in the controller - just waiting on an answer about whether they want me to combine or separate the two PRs. Thanks for your help @georgeawg
|

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.