Angular JS UI Router: Passing Objects between States

The UI router is (or should be) the go to routing mechanism for developing any AngularJS 1.x app that is moderately complicated. It handles nested routing gracefully and is great for state management.

Angular UI-Router is a client-side Single Page Application routing framework for AngularJS

This is not going to be an introduction to the UI router. You can awesome resources for that here, here and here. I am assuming that you are already familiar with the UI router.

One of the great features of the UI router, is that it works on a hierarchical state tree model. This means that the it treats your app?s Home page as one state, and User Details as another.

A state corresponds to a ?place? in the application in terms of the overall UI and navigation.

The UI router manages the transition between these states. It changes the URL when something changes programatically in your app, and vice versa ? it changes your app when the user hits a new URL.

One great feature with the UI router is it?s ability to handle URL parameters.

$stateProvider .state(?userDetail?, { url: ?/users/:userId?, templateUrl: ?user.detail.html?, controller: function ($stateParams) { // If we got here from a url /users/user007 expect($stateParams).toBe({userId: ?007?}); // Use param for achieving awesomeness } })

Here, we are passing the userId param to the URL and using it in our code. But from time to time, you come across situations where you want to pass more than just parameters.

Consider the situation where you have a user object that you have fetched from the backend using the userId like so:

{ userId: ?user007?, userName: ‘James Bond’, role: ‘Classified’, dob: ’18-Aug-1966′, bio: ‘Bond, James Bond’}

Another state in your app is a userEdit page where you need to display and edit all of the values in the user object.

$stateProvider .state(?userEdit?, { url: ?/users/:userId/edit?, templateUrl: ?user.edit.html?, controller: function ($stateParams) { // We do amazing stuff here } })

Now there are a few ways of going about this.

Pass the param

Navigate to the /users/:userId/edit page and then use the resolve feature to fetch the object from the server again (another HTTP request, yikes!).

  • This is great if performance is not of concern to you, because you?re targeting only users in developed countries with fast internet, and desktop browsers or if you?re using HTTP2 or SPDY.
  • This is also useful if you are expecting your users to reload the URL, and expect the same state every time.
  • However, this approach is not ideal if you want to save bandwidth and keep your app slick.

Use HTML5 Web Storage APIs

Store the user object in LocalStorage or SessionStorage and read from these, once you navigate to the userEdit state. If the user navigates directly to the userEdit state, use a resolve function on the state to pre-fetch the data.

  • This is good for performance, as we avoid an extra HTTP request by using HTML5 APIs
  • This takes us down the ugly path of global client side session variables, and is an imperative sort of solution.
  • We have to manually garbage clean the Web Storage.
  • This method can prove to be unreliable however, because HTML5 Storage APIs are implemented differently in different browsers.
  • There is usually a 10MB limit to data stored in Web Storage per origin (though ideally you wouldn?t reach the limit unless you haven?t bothered cleaning up after you).

Use UI Router?s $state.go

This is probably the best way to pass an object from one state to the next. The UI Router has a $state.go function which can take in 3 parameters like so:

$state.go(to [, toParams] [, options])// to: Destination State// toParams: Object containing Parameters to be sent to Destination// options: Configuration options

This means, we can just pass in our user object to the userEdit state as the toParams

$state.go(‘userEdit’, user);

If you want to trigger this from your HTML you can do the same thing

<button ng-click=”goToEdit()”>Edit</button>//Controller// user = {…}$scope.goToEdit = function() { $state.go(‘userEdit’, user);}

NOTE: You will need to use a resolve function to resolve the user data if the /users/:userId/edit URL directly.

  • This is clean and declarative.
  • You can save an extra HTTP request.
  • There is no extra session variables and garbage cleaning required

Leave a comment if there are better ways to handle this! I am just learning.

Happy coding!

16