Archive for April, 2011

ASP.NET MVC3 JSON decimal binding woes

I just stumbled onto this bug the other day and I figured I would share. Given the following controller action and ajax call, what would you expect the value of deez to be in the BreakStuff action?

//Model to be bound.
public class Decimals {
   public decimal d1 { get; set; }
   public decimal d2 { get; set; }
   public decimal d3 { get; set; }
}
//Controller action to check the binding
public ActionResult BreakStuff(Decimals deez) {
   return new EmptyResult();
}
//Ajax call to controller action above
$.ajax({
   type: "POST",
   url: "/Home/BreakStuff",
   contentType:'application/json',
   data:JSON.stringify({d1:42.00,d2:3.14,d3:'17'})
});

A screenshot says a thousand words:

Model values before adding DecimalModelBinder

What happened to d1? The resulting JSON from JSON.stringify({d1:42.00,d2:3.14,d3:'17'}) ends up being {"d1":42,"d2":3.14,"d3":"17"} Cool right? Mostly, except that once this value gets server side, that 42.00 is now seen as an int (42) and the default model binder doesn't want to stuff that into my decimal type. Bummer. Not exactly what I was expecting.

Phil Haack to the rescue! After doing some searching I stumbled across this post which fixes binding to string values with comma separators. It solves the same problem I'm having, so I'll repost the bits here:

using System;
using System.Globalization;
using System.Web.Mvc;

public class DecimalModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext,
                                    ModelBindingContext bindingContext) {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;
        try {
            actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
                CultureInfo.CurrentCulture);
        }
        catch (FormatException e) {
            modelState.Errors.Add(e);
        }

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        return actualValue;
    }
}

Then stick this into your Global.asax.cs Application_Start:

protected void Application_Start() {
    ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
    // Your other stuff goes here.
}

Once I did that and re-ran the code above, everything works as expected. See for yourself:

Model values after adding DecimalModelBinder

Playing With Knockout.js

When I was in grade school we used to do these fun run things. Basically, we begged all of our family to give us money for every lap run. Then we would go run on the hot blacktop for 30 minutes. In exchange for all of this we were able to get stupid ribbons and prizes. It was anything but fun.

I'm not sure why this example popped into my head when it came time to create an example to show off the simplicity and power of Knockout.js. Regardless, it did and you can see the result below.

I like this example because it shows bindings to a couple of input types. The lap count at the top is multiplied by the Pledge Per Lap in each row. The values are summed across each row and down the columns.

Once you've played with my silly example, click on the html and js tabs and let the simplicity soak in. There is almost no code there. I didn't have to handle change and click events. I didn't need to do anything special when a row gets added to recalculate totals. Even the empty grid message was a snap, look at that easy visible binding! It. Just. Works.

What are you waiting for? Go read more about Knockout.js now.