Fixing UI-SREF-ACTIVE – Specifying a Default Abstract State

I’ve recently begun working with Angular and by extension Angular UI-Router. The fact that you are reading this means that you likely have as well. That said, let’s all pause for a moment and cry together. I know it’s hard. You will get through it. It will be ok. We can do this.

Basic ui-sref-active Usage

One of the things that UI-Router gives you is the ability to add a class to an element if that elements state is currently active. This is done via the ui-sref-active directive.


<ul class="nav navbar-nav" ng-controller="navController">
   
   <li class="nav-node nav" ui-sref-active="active"><a ui-sref="home">Home</a></li>

   <li class="nav-node nav" ui-sref-active="active"><a ui-sref="notHome">Not Home</a></li>

</ul>

So above we have some basic navigation with two states. The home state and the notHome state. The ui-sref-active directive takes care of adding the active class to whichever li contains the state that is currently active.

The Problem with Abstract States

The problem is that the ui-sref-active directive does not work correctly (or as we expect) when the parent state is an abstract state.

Let’s say you want to expand your “home” state a bit. Maybe you want to add a “dashboard” state and from there link to a “messages” state. You might set up your $stateProvider a bit like this.

$stateProvider
	.state("home",
	{
		abstract: true,
		url: "/home"
	})
	.state("home.dashboard", {
		url: "/dashboard",
		views: {
			"content@": {
				templateUrl: "app/home/dashboard.html",
				controller: "DashboardController"
			}
		}
	})
   .state("home.messages", {
		url: "/messages",
		views: {
			"content@": {
				templateUrl: "app/home/messages.html",
				controller: "MessagesController"
			}
		}
	});

You’ll see we’ve setup home as an abstract view. By default we want to land on our home.dashboard state. We also want ui-sref-active to set the active class on our “Home” link regardless of which child state we are on.


<ul class="nav navbar-nav" ng-controller="navController">
   
   <li class="nav-node nav" ui-sref-active="active"><a ui-sref="home.dashboard">Home</a></li>


   <li class="nav-node nav" ui-sref-active="active"><a ui-sref="notHome">Not Home</a></li>

</ul>

You will notice that in the code above we are now using ui-sref to link to  home.dashboard. This is where the problem with ui-sref-active crops up, it will only show the active class if the state is home.dashboard. We want the active class to appear on any child of the “home” state. As it is, the ui-sref-active directive will not detect home.messages as active. So the question becomes, “how can we fix ui-sref-active so that it detects a parent abstract state”?

Fixing ui-sref-active

The answer comes from Tom Grant in the form of a comment on a GitHub issue.

Tom informs us that there is an undocumented built in solution to this ui-sref-active problem. The solution, he says, is to “use an object (like with ng-class) and it will work”.

Code examples that Tom provides:

<!-- This will not work -->
<li ui-sref-active="active">
   <a ui-sref="admin.users">Administration Panel</a>
</li>

<!-- This will work -->
<li ui-sref-active="{ 'active': 'admin' }">
   <a ui-sref="admin.users">Administration Panel</a>
</li>

That’s it. Now we can link to children of abstract ui-router states and ui-sref-active will behave the way we expect it should.

JavaScript Scoping. Callbacks and Loops

I just ran into this issue last night. The problem: I had a loop that was adding a callback to a method. Something like this:

for(var i=0;i<10;i++){
    $myElement.on('some-event', function(){
        DoSomethingWith(i);
    });
}

What I expected was that the value of the i variable at the time it was called would be used in my callback method. However, this was not the case… the i variable was the same in every single callback.

See this JSFiddle for an example.

The reason for this? JavaScript variable hoisting.  Before your code is executed it is scanned and the variables are processed. This has the effect of moving your variables to the top of the current function regardless of where in the function they are defined. (Except for in cases where you are implicitly declaring global variables).

So, in our situation we’ve defined var i. This is processed before the loop is processed and it is as if we wrote this:

var i;
for(i=0;i<10;i++){
    $myElement.on('some-event', function(){
        DoSomethingWith(i);
    });
}

Now it becomes a bit more clear why we are running into the issue with i being the same. The reason is because by the time the callback is executed the for loop has already run and the value of the i variable is already 10.

The solution, as far as I can tell, is to use an IIFE to scope the variable correctly in order store the current value for later. It looks ugly and it feels hacky… but it seems to be what is necessary. Update: It appears that you can also use .bind to set the value correctly as well.

var i;
for(i=0;i<10;i++){
    $myElement.on('some-event', 
        (function(i){
            return function(){
                DoSomethingWith(i);
            };
        })(i);
    );
}

And the JSFiddle to demonstrate.

Example With .bind

var i;
for(i=0;i<10;i++){
    $myElement.on('some-event', DoSomethingWith.bind(undefined, i));
}

Deserializing JSON to a .NET Object

Generally in .NET MVC you would use the default model binders to deserialize JSON to a .NET object. However, there are some cases, involving “complex” collections, where this becomes a bit tedious to do. Microsoft provides a few ways around this, but none are satisfactory. Unlike the default model binders, the JavaScriptSerializer provides support for deserializing JSON to a .NET object with complex collections.

Take the following simple class and method signature for example:

// simple class
public class SaveInformation
{
    public string Name { get; set; }
    public Dictionary<string, List<SaveItem>> Components { get; set; }
}

// method signature
public ActionResult Save(SaveInformation saveInformation)

Below I’ve included some simple JSON that we will pass to .NET via AJAX:

{"SaveToSession":false,"Name":"My Stupendous Thing","Components":{"Component1":[{"ProductId":"1234"}]}}

We are passing the JSON to .NET using AJAX with the “application/json” contentType. Because we are using the “application/json” contentType, .NET will automatically call the JsonValueProviderFactory and map the information over to our SaveInformation model. It seems pretty straight forward, the Save method will receive a SaveInformation object when it is called.

Not so straightforward unfortunately, the Save method does receive a SaveInformation object. If we inspect our SaveInformation object we see the Name is populated perfectly fine, but the Components dictionary ends up being null.

The reason for this? It seems that the JsonValueProviderFactory doesn’t fully support pure JSON syntax. In order for .NET to properly parse a complex collection, you actually have to give it a numerical index.

From http://msdn.microsoft.com/en-us/magazine/hh781022.aspx:

Though it’s somewhat counterintuitive, JSON requests have the same requirements—they, too, must adhere to the form post naming syntax. Take, for example, the JSON payload for the previous UnitPrice collection. The pure JSON array syntax for this data would be represented as:

[
    { "Code": "USD", "Amount": 100.00 },
    { "Code": "EUR", "Amount": 73.64 }
]

However, the default value providers and model binders require the data to be represented as a JSON form post:

{
    "UnitPrice[0].Code": "USD",
    "UnitPrice[0].Amount": 100.00,

    "UnitPrice[1].Code": "EUR",
    "UnitPrice[1].Amount": 73.64
}

I know right? It was hard for me to read this as well due to the anime-sized tears in my normal-sized eyes. The problem is, no browser I know of has a JSON.serializeForDotNet function. So this leaves us with two options, make our own JSON serializer, or make our own ValueProvider/ModelBinder. Neither of these options sounded very appealing to me, so I went with the hidden third option. Pass in a string 🙂

You can use the JavaScriptSerializer class, located in System.Web.Script.Serialization, to deserialize JSON information and, as an added bonus, it actually handles properly formatted pure JSON!

So, the new method looks like this. We pass in a string value and deserialize it using the JavaScriptSerializer. Now we receive a SaveInformation object with a fully populated Components dictionary.

public ActionResult Save(string saveInformation)
{
    // instantiate a JavaScriptSerializer
    JavaScriptSerializer serializer = new JavaScriptSerializer();

    // use the JavaScriptSerializer to deserialize our json into the expected object
    SaveInformation saveInformationObject = serializer.Deserialize<SaveInformation>(saveInformation);

Use a JSON Parser to Visualize a JSON String

Just noting this here for future reference.

Whenever I am working with JSON I find it useful to be able to parse it. Now I can certainly parse it with Chrome’s inspector and see it in the console… but it’s nice to have an easy online tool to use as well.

The tool I use, and the tool that has helped me find several issues in the past, is JSON Parser Online. It’s really nice to be able to see the JSON string on the left and the parsed object on the right.