This blog has, IMO, some great resources. Unfortunately, some of those resources are becoming less relevant. I'm still blogging, learning tech and helping others...please find me at my new home on http://www.jameschambers.com/.

Friday, April 23, 2010

ASP.NET MVC and jQuery Part 3 – Model Binding

This is the third post in a series about jQuery and ASP.NET MVC 2 in Visual Studio 2010.

100% of the developers I have talked to this morning are enjoying jQuery and how it makes ASP.NET MVC sites a treat to work on. Of course, the sample size was one (1) developer, and he’s writing this post. Just sayin’.

In this article I’m going to cover some of the model binding scenarios that work seamlessly with these two technologies, starting off with some background and a sample, then progressing to more complex concepts.

Model Binding Basics

Model binding occurs in the processing of a request in the MVC framework, after routing and the creation of the controller. After the controller comes out of the factory, reflection kicks in to match up the request from the routing engine (as a result of some client action) with any parameters in the HTTP request.

Said another way: form values and querystring parameters get automatically turned into .NET objects in the MVC framework. Cool.

To provide a simple overview of how model binding works, we’re going to set up a quick demonstration. Start up the IDE and create an ASP.NET MVC 2 Web Application. Delete the Index file (we’re going to create our own later). Here’s the steps we’re going to follow:

  • Create a new model called Person (and build our project)
  • Update our Controller to setup an “empty” person for our view
  • Create a strongly-typed view to create a person
  • Create an action method on our Home Controller to capture the ‘create’
  • Let the MVC framework do the rest

We’re not actually going to persist anything here, this is just a show-and-tell.

The simple parts

Navigate to your Models folder and right-click to Add –> Class called Person. Complete the class out as follows:

image

We need to build our project here (SHIFT + CTRL + b) for the IDE to be able to see our class in later steps. The type-awareness from reflection requires that our type exist in an assembly.

Pop over to Controllers –> HomeController and delete the ViewData assignment. We’re going to create a new Person here and return it with the view. Our action looks very simply like so:

image

image Right-click anywhere in the method and select Add View… from the context menu. We are adding a view named Index, setting it to be a strongly-typed view and setting the view content to Create. This will allow code generation to output the basics of the form we’re going to use.

Note that here, or anytime you try to add a view, that if your model hasn’t been updated by building your project your types won’t appear in the View Data Class drop-down. You can cancel, build, and re-open the dialog if that is the case.

The page that is generated is super handy as a starting point. Even if it doesn’t save me much time (after all the changes and deleting I do) for some reason it just makes me feel better that the computer had to do some work too.

Let’s clean it up a bit. Remove the validation controls, delete the DIV with the ‘back to list’ link in it and change the H2 to something more interesting.

Because I’m not going to be posting back to my Index action method, I need to also update the using statement to have to correct calls created for submitting the form:

Html.BeginForm("CreatePerson", "Home")

Finally, let’s add a bit of code with the corresponding method, CreatePerson, and a breakpoint in our Home controller:

image

The above doesn’t actually do anything (and won’t go anywhere past the breakpoint), but if you pause where I suggest and evaluate the Person object or the name that was assigned the string, you’ll see the values populated.

How did the Model Binding Work?

There are actually a ton of in-depth explanations out there, but here is the summary of what we’ve done and what the MVC framework does to support model binding:

  • We have a class with public properties
  • The template engine created a page for us with the HTML helper TextBoxFor
  • TextBoxFor uses the name of the public properties to generate a form input with the same name
  • The template creates a form to host those controls, which we directed to our controller and action
  • When written to the browser, the form action is set to “/Home/CreatePerson” using the POST verb
  • The MVC framework routes the form submission to the Home controller, then uses reflection to find the CreatePerson method
  • Because the method accepts a Person object as a parameter, the MVC framework evaluates the HTTP request (querystring parameters, form values) and then hydrates an object for us

Now, Let’s Make it Interesting

So far, this has just been a demonstration of simple model binding in the MVC framework. Let’s get jQuery involved.

  • Change the controller action to a partial view result
  • Create a partial view that reveals the created person
  • Add jQuery and jQuery UI to the Site.Master file
  • Remove the using statement from the Index page
  • Modify the input so it’s easier to select in jQuery
  • Add a DIV to store the results of the create
  • Add a calendar selector for the birthdate
  • Submit the form via jQuery when the user clicks the button
  • Clear the form when the partial view result comes back, and display the result

Let’s start by changing the method to only accept POSTs and simply returning the Person object we were passed in to the PartialView.

image

image Right-click anywhere on the method and select Add View… from the context menu.

This time we’ll create the view as a partial, it will be strongly-typed to Person again, but we’ll set the View Content to Details.

The tooling support for the MVC Framework in Visual Studio 2010 will then create the ASCX (server control) for us with all the fields displayed.

Delete the P tag at the end of the generated control with the ActionLinks in it, leaving only the control tag at the top and the FIELDSET control.

Next, pop into Views –> Shared –> Site.Master and add jQuery to the HEAD of the master page. Let’s also add jQuery UI at this time to open the doors to some other augmentations. For full functionality you’ll need the CSS and the images folder in your project as well, and the CSS linked in the HEAD of the master page.

In Index.aspx, remove the using statement so that the form is no longer generated. This is easy if you use Visual Studio 2010’s collapsible regions (you can collapse the FIELDSET tag to see the whole using construct).

Add an ID with a value of “create-person-button” to the submit button at the bottom of Index.aspx. Lastly, before we get scripting, add a DIV to display the partial view when the person is ‘created’. These last two bits should look like the following:

image

On to the script

jQuery gives us a way to easily make AJAX requests via POSTing to the server. We pass in a map of data that the server is expecting. The result can be passed to a function that evaluates the results.

With the inputs setup the way they are and our placeholder…uh…holding a place, we’re all set to execute our POST.

We’ll write a helper function that will process the results of the POST operation and, of course, a jQuery ‘document ready’ event handler to setup the calendar and trap the submit button click.

image

Most of the code should be easy enough to follow along. The one interesting bit is that the data map need not be constructed with proper case. That is, if your fieldname is FirstName you can use FiRsTnAmE as the name in the data map and that is okay.

Run the app and enjoy!

The jQuery UI datepicker in action:

image

The form cleared after submit and the results of the POST displayed:

image

Some Caveats

At this point we don’t have any validation in place, we’re not saving to a database of any kind and we’re hooped if there’s a server-side error.

Unfortunately the jQuery POST doesn’t play nicely with unhandled exceptions that are thrown on the server. $.post is shorthand for a sub-set of the functionality (with some presets) of $.ajax, and only maps a handler for the success event (not error events).

To see this in, well, inaction change your CreatePerson action to the following:

image

This will instruct the MVC framework to pass the person object through to a view named “foo”, which doesn’t exist. If you run the form at this point, and submit, you’ll see no errors (unless you attach a debugger, like FireBug, to the script).

More to Come

In my next article I’m going to look at more complex data scenarios, like selections from a list and posting arrays back to the controller. I’ll also integrate some other goodies, like jQuery UI’s autocomplete, to help users pick a color.

Resources

6 comments:

  1. Good article, thanks for taking the time to post it!

    ReplyDelete
  2. I agree. Thank you for making this simple example!

    ReplyDelete
  3. Great article. Thanks! I have one question though... What if I already have a display template for the person class, stored in /Views/Home/DisplayTemplates/Person.ascx

    Is there a way to use that partial view instead of the CreatePerson.ascx? MVC only seems to look in the /Views/Home/ folder and not in the DisplayTemplates folder when I use
    "return PartialView("Person", person);"

    ReplyDelete
  4. If I understand your question correctly, you should just be able to move your Person.ascx control to the Views/Shared/DisplayTemplates section and MVC routing will pick it up. If not, you can fire me an email and I would be happy to help out further!
    -James

    ReplyDelete
  5. Very well explained.

    ReplyDelete
  6. Very good example...

    ReplyDelete