sma
sma

Reputation: 9597

Angular UI Router - Default Parameter

Is there anyway to specify a default parameter for every route using the Angular UI Router?

My app is entered through the context of another application by selecting a user and then navigating to my application. The URL in my application will always have the user ID in the URL so that people can bookmark the URL, email it, etc. So, as you navigate around, the URL always follows a scheme of:

#/{userId}/view/...
#/{userId}/edit/...

etc.

This userId will always be the same for a user inside the app for any route they go to. If they happen to log out, go back to the main app, select a new user and come back to my app, this userId will change, but will be the same value for every route.

Is there anyway to read this value from say a service/factory and then plug it into every route?

EDIT:

I should mention I want to avoid having to explicitly set this parameter on every route when I navigate to a state. For example, I don't want to have to do ui-sref="new-state({userId : blah})" every time I navigate to a new state. That userId will never change in the context of my application.

EDIT AGAIN:

I actually went about this a different way concerning the requirement to not have to send 'userId' to every route manually. Instead of using a directive, I used a $provide.decorator to add this functionality to the 'go' method. I've added an answer below to what I did.

Upvotes: 9

Views: 7746

Answers (3)

sma
sma

Reputation: 9597

Instead of writing a directive that handled the auto-sending of userID, I used $provide.decorator as follows:

app.config(['$provide',
    function($provide) {
        $provide.decorator('$state', function($delegate, UserService) {

            // Save off delegate to use 'state' locally
            var state = $delegate;

            // Save off reference to original state.go
            state.baseGo = state.go;

            // Decorate the original 'go' to always plug in the userID
            var go = function(to, params, options) {

                params.userID = UserService.userID;

                // Invoke the original go
                this.baseGo(to, params, options);
            };

            // assign new 'go', decorating the old 'go'
            state.go = go;

            return $delegate;
        });
    }
]);

I got the idea from this post:

Changing the default behavior of $state.go() in ui.router to reload by default

Upvotes: 3

scarlz
scarlz

Reputation: 2512

You can declare an abstract parent state from which child states inherit:

$stateProvider
  .state('user', {
     url: '/:userid',
     abstract: true,
     resolve: 
       // assuming some kind of User resource factory
       currentUser: function($stateParams, User) {
         return User.get($stateParams.userid);
       }
     }
   })
  .state('user.view', {
     url: '/view', // actual url /:userid/view
     controller: function($scope, currentUser) {
       // currentUser resource available
     }
   });
  .state('user.edit', {
     url: '/edit', // actual url /:userid/edit
     controller: function($scope, currentUser) {
       // currentUser resource available
     }
   });

In terms of navigating to a state, you need to pass in the desired user:

$state.go('user.view', {userid: 'myuserid'});

As a consequence it might make sense to create some kind of .go() wrapper method on your currentUser service, so that you needn't specify the user id each time.

UPDATE:

To counter the problem posted in your edit, you could introduce a directive like this:

angular.module('app')
  .directive('userSref', function($state) {               
     return function(scope, elem, attrs) {
       var state = 'user.' + attrs.userSref;

       elem.bind('click', function() {
         $state.go(state, {userid: $state.params.userid});
       });           

       scope.$on('$destroy', function() {
         elem.unbind('click');
       });
     };
  });

Then, any future links to user-based states can be done so with:

<a user-sref="view">View User</a>

Upvotes: 7

Sunil D.
Sunil D.

Reputation: 18193

You can use the "nested states" and "resolves" features of UI-Router to create a hierarchy of states in your app. You'll define a top level state that resolves the userId. Then define any number of child states, and they will automatically inherit the "resolved" userId.

Check out this page of the documentation, in particular the section titled "Important $stateParams Gotcha". I will paste the two code snippets from that page here.

Incorrect method:

$stateProvider.state('contacts.detail', {
   url: '/contacts/:contactId',   
   controller: function($stateParams){
      $stateParams.contactId  //*** Exists! ***//
   }
}).state('contacts.detail.subitem', {
   url: '/item/:itemId', 
   controller: function($stateParams){
      $stateParams.contactId //*** Watch Out! DOESN'T EXIST!! ***//
      $stateParams.itemId //*** Exists! ***//  
   }
})

Correct method using "resolves":

$stateProvider.state('contacts.detail', {
   url: '/contacts/:contactId',   
   controller: function($stateParams){
      $stateParams.contactId  //*** Exists! ***//
   },
   resolve:{
      contactId: ['$stateParams', function($stateParams){
          return $stateParams.contactId;
      }]
   }
}).state('contacts.detail.subitem', {
   url: '/item/:itemId', 
   controller: function($stateParams, contactId){
      contactId //*** Exists! ***//
      $stateParams.itemId //*** Exists! ***//  
   }
})

Since the "contactId" parameter is resolved by the parent state, the child state will inherit that.

Upvotes: -1

Related Questions