[MVC3] ViewModel Binding To Controller–POST Methods


Now we should be writing strongly typed Views based on our Models. Now in some cases you may want to use the ViewModel pattern, which allows you to create a View based on:

 

a) One Model and some additional properties

b) Two or more combined Models

c) Two or more combined Models plus some additional properties.

These ViewModel classes would sit in a folder structure, you guessed it, ViewModels in your Visual Studio Project.An example would be my RegisterViewModel i have created:

 

 

public class RegisterViewModel {
    public RegisterModel RegisterModel { get; set; }
    public string UniqueKey { get; set; }
    public bool Thumbnail { get; set; }

}

 

 

So this is quite straight forward really. I have a RegisterModel which is the MVC default Membership model for handling when a new user Registers. I have a property called UniqueKey which will hold the of a created user, and a Boolean field Thumbnail which will be set to true if he want thumbnail images or false if we don’t.

So this is nice a simple then. So what this means that in our GET Action controller for register we can setup the required values. In this instance we only set the Thumbnail property:

 

// GET: /Account/Register public ActionResult Register()
{
    var viewModel = new RegisterViewModel {
        Thumbnail = true };

    return View(viewModel);
}

 

 

Then in our Review View, we need to declare our ViewModel instead of our RegisterModel, plus all our Model fields and the additional properties are parsed:

 

@model MvcImage.ViewModels.RegisterViewModel @using MvcImage.Models
@using Bradaz.OpenSource.MVC
@using Bradaz.OpenSource.MVC.HTMLHelpers

@{     ViewBag.Title = "Register";
} @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
    <div> <fieldset> <legend>Account Information</legend> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.UserName)
                @Html.ValidationMessageFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Email)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.Email)
                @Html.ValidationMessageFor(model => model.RegisterModel.Email)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Password)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.Password)
                @Html.ValidationMessageFor(model => model.RegisterModel.Password)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.ConfirmPassword)
                @Html.ValidationMessageFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-label"> @Html.Label("Avatar")
            </div> <div class="editor-field"> @Html.UploadImageFor(model => model.UniqueKey,thumbnail:true)
            </div> <div class="editor-field"> @Html.HiddenFor(model => model.Thumbnail)
            </div> <p> <input type="submit" value="Register" /> </p> </fieldset> </div> }

 

NB: I have changed my UploadImageFor HTML Helper to include Thumbnail support. This will be documented separately and released under my Open Source MvcImage Project.

So this all sounds simple right? Well you may get into a bit of a pickle (like me) when handling the POST Register ActionMethod. This would be peoples first attempts, which are both wrong:

Attempt 1

[HttpPost]
public ActionResult Register(RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

Attempt 2

[HttpPost]
public ActionResult Register(RegisterViewModel registerviewmodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

 

These are wrong if you wish to access the additional properties and the ResgiterModel within the RegisterViewModel. Let me explain:

 

Attempt 1 – You would be able to access the RegisterModel properties but not the Thumbnail property.

 

Attempt 2 – Your RegisterModel would be null and you wont be able to access the Thumbnail.

 

After these attempts most people scratch their head, curse, then head over to ASP.NET/FORUMS or stackoverflow for an answer. Well it took me a lot of scratching, googling and forum posting (plus nagging my poor friend Shawson!) to find the answers. The answer lies in how MVC binds the Models. The best place to start is in Fiddler, which is a free application for debugging HTTP and is very useful. Here is a glimpse of the form POST when i hit the Register button on my view:

 

 

Capture_Blog

 

As you can see our RegisterModel properties are prefixed with “RegisterModel” and our Thumbnail is just Thumbnail. So if you apply the MVC Binding on this, in the two attempts above, in the first attempt, MVC recognises RegisterModel as these have been binded from the HTTP post data, so are available. Thumbnail though isn’t available in the RegisterModel declaration in the class so this is ignored. In the 2nd attempt there is no RegisterViewModel in the POST so the binding will fail, thus nothing is available and you will get null fields.

 

The answer is the following:

[HttpPost]
public ActionResult Register(bool Thumbnail, 
[Bind(Prefix = "RegisterModel")]RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///-Code something }

 

So here we declare Thumbnail as its own separate parameter and Bind the RegisterModel with a Prefix “RegisterModel” using the BindAttribute.  This allows us toaccess our RegisterViewModel properties AND Models within our controller quite happily.

I hope this helps you when using ViewModels.

Enhanced by Zemanta

6 thoughts on “[MVC3] ViewModel Binding To Controller–POST Methods

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s