Phoebe Li
Phoebe Li

Reputation: 9925

$state are not populated in resolving promises in Angular routes

I came across this situation where I can see $stateParams gets populated in one place but not in another. I'm kind of newbie to angular-ui-router so any help will be appreciated.Thanks !

In the resolve block of the following state, I injected $stateParams as a dependency in the function for data 'campaign' and the $stateParams is populated.

.state('campaign', {
          url: "/campaign/{campaignId:int}",
          templateUrl: campaign_template,
          controller: 'CampaignCtrl',
          parent: 'org',
          abstract: true,
          resolve: {
            campaign: function(CampaignService, $stateParams) {

              console.log('$stateParams is populated here!', $stateParams)

              return CampaignService.get($stateParams.campaignId)
                .then(function(campaign) {
                  return campaign;
                });

            }
          }

Inside the CampaignService function, however, I require $stateParams but it's empty. I'm confused because I'm assuming since it's populated when I injected it in the resolve block, it should be the same no matter where else I get it again.

  .service('CampaignService', function($injector, $q) {
            this.get = function() {
              var $stateParams = $injector.get('$stateParams');

              console.log('$stateParams is empty here!', $stateParams);

              var deferred = $q.defer();
              setTimeout(function() {
                deferred.resolve({
                  name: 'campaignName'
                });
              }, 1000);

              return deferred.promise;
            }
          })

Upvotes: 2

Views: 156

Answers (2)

georgeawg
georgeawg

Reputation: 48968

I'm assuming since it's populated when I injected it in the resolve block, it should be the same no matter where else I get it again.

The $stateParams injected into the resolve block is the proposed future state. At that point in time the application is still using the old state. And will remain in the old state if any of the resolve promises are rejected.

Under the hood, the $state service creates a local version of $stateParams that it injects in the resolve function:

  var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
  var locals = { $stateParams: $stateParams };

  // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
  // We're also including $stateParams in this; that way the parameters are restricted
  // to the set that should be visible to the state, and are independent of when we update
  // the global $state and $stateParams values.
  dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
  var promises = [dst.resolve.then(function (globals) {
    dst.globals = globals;
  })];

https://github.com/angular-ui/ui-router/blob/legacy/src/state.js#L1427-L1437

The solution is to pass the proposed future $stateParams to the service.

Upvotes: 1

Troy Carlson
Troy Carlson

Reputation: 3121

Your service probably shouldn't care about state parameters. You are already passing in the campaignId value inside of your state definition so in order to consume that within the service you could modify it like this:

.service('CampaignService', function($injector, $q) {
    this.get = function(campaignId) {

        console.log('campaignId = ' + campaignId);

        var deferred = $q.defer();
        setTimeout(function() {
            deferred.resolve({
                name: 'campaignName'
            });
        }, 1000);

        return deferred.promise;
    }
})

Upvotes: 1

Related Questions