0

Using ui-router and attempting to pass URL query parameters to the controller in the .config of the module, using a .service that has been injected into the resolve argument of the .state definition.

Preferred Method - (not working reliably)

.state('list', {
    url: '/list?country&state&city',
    templateUrl: "list.html",
    controller : 'myController',
    resolve: {
          currentUrlTypeParam : ['myServices', function(myServices){        // object variable that can be injected into specified controller
              return myServices.getUrlTypeParams();
            }]
        }
  })

While the documentation says that only Constant and Provider recipes can be injected into the .config function of a module, I'm following this method of injecting a .service into the resolve argument of the .state definition.

The reason I'm using a service is that I have a number of states with the same URL query parameters - so it makes sense to write the function once as a service provider rather than repeating it.

I'm using resolve rather than just injecting $stateParams directly into the controller so that I can complete server calls during config.

Here's a plunker of this (showing 3 possible states with the same URL parameters) - however it doesn't work reliably - the correct URL params seem to arrive on a 2nd click to a different URL in the same state.

Even more frustrating is that while the $stateParams seems to arrive correctly (on 1st click) in the .service function - but the values come up as undefined when trying to access them - check out the plunker console.log to see this strange phenomena !

eg
console.log will output $stateParams in the .service function as:
{country: "US", state: undefined, city: undefined}
however when I call $stateParams.country within the function i get undefined ??

Alternative Messier Method - (working fine)

The alternative is repeating the same large .service code within the resolve argument of every state definition - which works reliably, but bloats the code (...and this is a simplified example of the states / params I am dealing with).

.state('list', {
    url: '/list?country&state&city',
    templateUrl: "list.html",
    controller : 'myController',
    resolve: {
        currentUrlTypeParam : ['$stateParams', function($stateParams){
            var urlTypeParameter = {}                                                                               // define an object that we'll return as the value for $scope.loadMethodParameters
            if($stateParams.country && typeof $stateParams.country !== undefined){
                urlTypeParameter.type = 'country';
                urlTypeParameter.parameter = $stateParams.country;
            } else if($stateParams.state && typeof $stateParams.state !== undefined){
                urlTypeParameter.type = 'state';
                urlTypeParameter.parameter = $stateParams.state;
            } else if($stateParams.city && typeof $stateParams.city !== undefined){
                urlTypeParameter.type = 'city';
                urlTypeParameter.parameter = $stateParams.city;
            } else {
                urlTypeParameter.type = 'default';
            }
            return urlTypeParameter;
        }]
    }
})

See this plunker for a rather ...inelegant way of achieving the same thing.

Community
  • 1
  • 1
goredwards
  • 2,486
  • 2
  • 30
  • 40

1 Answers1

1

The reason why your service doesn't work is because you are trying to inject $stateParams, rather than passing it into the service function itself.

Rather than injecting $stateParams into your service and running return myServices.getUrlTypeParams();, try passing $stateParams into your service functions. For example:

resolve: {
    currentUrlTypeParam : ['$stateParams', 'myServices', function($stateParams, myServices){        // object variable that can be injected into specified controller
        return myServices.getUrlTypeParams($stateParams);
    }]
}

Then you will have to modify your service to not inject $stateParams, but instead pass it into your service function:

this.getUrlTypeParams = function($stateParams){ ... }
Matt Way
  • 32,319
  • 10
  • 79
  • 85
  • thanks i'll give this a go - how is it that using my 'preferred' method `$stateParams` arrives perfectly within the service, but its values can't be accessed ? – goredwards Sep 30 '16 at 23:46
  • It could be because of *when* $stateParams is updated. Anyway while $stateParams could be considered an injectable module throughout an app, it really should only be injected in a controller or a resolve, and nowhere else. – Matt Way Oct 01 '16 at 00:02
  • Actually, I just realised it is because you are logging the $stateParams object. Whenever you call `console.log()` what you see is not the object at the time of the log, but rather at the time of viewing the object in the console. So it may appear that the values are there and have been updated, when in fact they haven't. You can see this by instead of logging the whole params object, simply log one of the children like, `console.log($stateParams.country)` – Matt Way Oct 01 '16 at 00:04
  • yes you're right - http://stackoverflow.com/a/18565594/3092596 - I've updated the original plunker if you want to include it in your answer: http://plnkr.co/edit/ZnERL1OsopHopXiLXJC5?p=preview – goredwards Oct 01 '16 at 20:22