This is the fourth post in a series about jQuery and ASP.NET MVC 2 in Visual Studio 2010.
In my last post I covered the simple scenario of a Person object getting POSTed to an MVC controller using jQuery. In this article, I’m going to look at three other, more complex examples of real-world model binding and where jQuery might fit in the mix.
The model binding scenarios I’ll cover are:
- Sorting a list with jQuery UI support and submitting IDs to the controller for processing.
- Using jQuery to augment the user experience on a list of checkboxes (supporting check all/none) and allowing MVC to handle the elegance on it’s own.
- Give users the ability to build a list of items on a complex object, then submit that object to an MVC controller for processing.
The entire working solution is available for download at the end of the file.
In the first two examples here I have lists of data that are statically programmed into the page. From my earlier posts in the series you can see how easy it is to generate partial views that would drive the UI elements from a database or other backend. For brevity, I’m leaving those elements out of this example.
The jQuery UI library provides the sortable() function which transforms the children of a selected element in the DOM to draggable, orderable items for the user to manipulate.
The sortable() extension also provides a mechanism for us to capture the order of the list via the toArray method.
Using the jQuery.ajax() method, we submit the results of the users’ efforts via post to a controller action. For the purpose of this post, I’m not going to do any processing in the controller, but you can set breakpoints to see the data getting hydrated into the models.
Let’s start by setting up the controller action, which accepts a list of ints as a parameter to capture the order of the IDs.
Yes. It’s that simple. Now, you’d likely want to do some processing, perhaps even save the order to a database, but this is all we need to catch the data jQuery.ajax() will throw at us.
The juicy bits in jQuery are as follows:
Note: In order to make the results of the array compatible with the binding mechanism in ASP.NET MVC (as at MVC 2.0) we need to use the ‘traditional’ setting in $.ajax().
There are a couple of interesting things to note here:
- jQuery.ajax() by default makes requests to the current URL via get. My controller action that accepts the List<int> is the same name as the basic view. I change the type here to POST and the proper controller action is called.
- The ‘toArray’ returns an array of the selected element IDs.
- My unordered list contains list items with IDs that represent the unique items. In this case, they are integers stored as strings (by nature of HTML).
- The name of the list of IDs is passed in as the same name as the parameter in the controller action.
- When submitted to the controller, the MVC framework finds the appropriate method, looks at the expected parameter type and sees the array sent by the client. It then uses reflection and parsing (and maybe some voodoo) to coerce the values into the expected parameter type.
We can then use the list of IDs as required in the comfort of c#.
One of the interesting things that you can do, as the List<int> parameter is an IEnumerable, is that you can use LINQ to Objects on these guys without any effort. Thanks MVC Framework!
How do you submit a list of the selected checkboxes back to an ASP.NET MVC controller action? It’s all too simple. In fact, the only reason I mention it here is to highlight some of the simplicity we inherit when we use the MVC Framework.
I actually laughed out loud when I figured this one out. It’s that good (or, I’m that easily impressed).
For jQuery on this example, I’m only really going to use it to augment the user experience by providing a couple of buttons to check all or check none of the options.
We’ll use a standard HTML form and allow the user to select the items in a list they feel are appropriate. The form will be submitted via POST to our controller action (named the same as the ActionResult for the original View) and our parameter will be automatically populated for us.
Some things to point out at this junction:
- The values on the checkboxes here are the same strings that are displayed in the labels.
- I have given all the checkboxes the same name. When submitted, MVC sees these as some kind of enumerable thing and will then try to bind based on that.
- Optimus Prime is not a Jedi.
- This the name used for the checkboxes is the same name as the parameter in the controller action.
Packing a Complex Model
What about if you have a complex type with properties that can’t be expressed with simple HTML form fields? What if there are enumerable types as properties on the object?
Man, am I glad you asked! The ASP.NET MVC Framework is pretty smart about taking items off the client Request and building up your objects for you in the hydration process.
Here is my Suitcase class, with a List<string> that will contain all of the things that someone wants to take along on their vacation.
So how do we get those items into the object? The first step is to allow users to create them. We do this with a simple textbox and a button, rigged up to some jQuery script as follows:
When the user enters an item and clicks ‘Add to suitcase’ (or hits enter) we grab a reference to the textbox. Next, we use jQuery.append() to create a new LI element with the contents of the textbox. Finally, we clear out the value and return focus to the input field.
When the user is finished loaded up their bags, we need to create a data map that will be submitted. To simplify the process a little, we’ll first get that list of clothes together.
We first create an empty array. Next we use jQuery.each() to loop through all the returned elements – the list of LI elements that the user has created – and add the text of those LIs to the array.
Next, we POST the data back to the server:
Here are some observations:
- We’re POSTing and using the traditional setting so that the enumerated items are compatible with the current versions of jQuery and MVC.
- The names of the properties in the Suitcase class are the names of the values we use in the data map submitted by jQuery.ajax().
- As in the first example, jQuery.ajax() is posting to the default URL, which is the same URL as the view in this case. In the controller we differentiate the action with the HttpPost attribute and, of course, the Suitcase parameter.
When the data is submitted we see this in the controller action breakpoint:
And the contents of the Suitcase.Clothes property:
There you have it: the basics of advanced…stuff.
From here you should be able to work out most scenarios when building up objects in jQuery, submitting them to the controller and making a jazzier UI come together with jQuery UI while still using MVC in the backend.
Some things I’ve learned along the way:
- Remember to watch the names of your variables in data maps! They have to match the parameters (or member properties) of the controller action.
- If you’re having trouble getting things to submit and you’re not seeing any errors, try attaching FireBug in FireFox to the browsing session and see what’s happening to your requests/responses.
- Make sure that you’re sending the values of the jQuery selections, and not the objects themselves if you’re having trouble binding.
- Don’t send: $(“#my-textbox”)
- Send: $(“#my-textbox”).val()