Hacking jQuery-Validate in ASP.NET MVC

You'll remember from my last post that I've been working on a small order-entry program in ASP.NET MVC. This is my first "from scratch" ASP.NET MVC application, so I've been learning some MVC and jQuery tricks along the way.

Here's a partial screenshot of the "new order" screen to refresh your memory:

A feed order

You'll notice that each silo has a "capacity", and I wanted to be able to limit the item quantity to that value. No sense in allowing a farm to order more feed than will fit into the silo!

ASP.NET MVC's support for data annotations (in the form of attributes) to enable validation is very funky. I can mark my Quantity property up like this:

[Range(0, 99)]
public int Quantity { get; set; }

... and the generated HTML will automatically apply an error to the text input when the user types a negative value or one above 99. The markup ends up looking something like this:

<input data-val-range="Value must be between 0 and 99."
       data-val-range-max="99" data-val-range-min="0" ... />

I can't remember the exact error message, but you get the idea.

This is all well and good for values that apply at compile time, but each one of my order items should have its "data-val-range-max" value set to the capacity of the silo, and I can't do that with a [Range] attribute.

I could have defined my own custom validation class and corresponding .js file to enable this sort of validation logic, but for now I have found a much easier way. In the Razor view that renders the form, I found the line the spits out the text input, and changed it to this:

@Html.TextBoxFor(m => m.Items[i].Quantity,
    new
    {
        maxlength = 2,
        data_val_range_min = 0,
        data_val_range_max = item.Silo.Capacity,
        data_val_range = "Quantity must be between 0 and " + item.Silo.Capacity + "."
    })

So I'm still telling Razor to generate a text input box for my "Quantity" property, but I'm adding some custom attributes to the generated HTML. It looks like this now:

<input data-val="true" data-val-range="Quantity must be between 0 and 12." 
       data-val-range-max="12" data-val-range-min="0"... />

In this case, the silo's capacity is 12 tonne, and so the Quantity input associated with that silo has had its maximum value set to 12 too.

This gives me client-side validation of each order item's quantity without having to define my own custom validation .js code! Of course I've mirrored the logic in C#, so if the form is somehow posted with invalid data it will still get rejected. Still, this simple workaround saved me quite a lot of time, and I hope it helps you too.

jquery asp.net-mvc razor
Posted by: Matt Hamilton
Last revised: 25 Oct, 2014 01:23 AM History

Trackbacks

Comments

nachid
nachid
17 Apr, 2011 07:10 AM

This is very smart.

Thank you for sharing this idea

I am , however, wondering if you are still using this line of code

[Range(0, 99)]
public int Quantity { get; set; }

In this case, validation attributes should override your @Html.TextBox attributes.

17 Apr, 2011 07:33 AM

Hi nachid,

No, I don't have the attribute on the property itself anymore. You might be right - I'm not sure which one "wins" in this case.

24 Apr, 2011 12:46 AM

Mixing View and Model is a bad idea. In this case (if you can't use attributes on Model), you can implement ModelValidator. It's a little bit harder, but allows you to keep Model and View separated.

sridhar chinta
sridhar chinta
21 Jun, 2011 07:21 PM

lol.. you are awesome. i was just looking for this since half hour

07 Sep, 2011 10:49 AM

Thanks for this!

The MVC3 design limitiation of only allowing model annotations to be design-time constants is constantly irritating to me, and this little hack makes decent client-side validation a hell of a lot easier.

I keep being tempted to spend some proper time rolling my own truly dynamic data validation system, but, as usual, time constraints and the need to get a product shipped override that.

No new comments are allowed on this post.