code blog foo - tag line bar

TextBoxFor Usage in ASP.NET MVC

In ASP.NET MVC, the Microsoft team has given us a very nice extension method called Html.TextBoxFor(). This method is much nicer than Html.TextBox(). Magic strings are needed when you use Html.TextBox, which makes refactoring a real pain...using Html.TextBoxFor() doesn't require any magic strings....which is a good thing. So lets see what TextBoxFor can do, shall we?





What TextBoxFor Does

I have the following strongly typed View Page:

    <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Customer>" %>
    <!-- notice that the ViewPage's ViewData.Model is of type Customer -->

Now let's say I want a textbox for the customer's first name. I can use the HtmlHelper TextBoxFor() method to create a textbox:

    <%= Html.TextBoxFor(customer => customer.FirstName, 
        new { @class="roundedTextBox" }) %>

The code above will generate the following html:

    <input type="text" name="FirstName" id="FirstName" class="roundedTextBox" value="John" />

Saving a Customer - View Page

So here's the thing...that's nice and all...but let's take the following scenario. I want to update some customer information. Here is what the view code would look like for generating a save customer form.

<% using(Html.BeginForm("Save", "Customer", FormMethod.Post)) { %>
    <!-- Html Helper HiddenFor() and TextBoxFor() are used -->
    <%= Html.HiddenFor(customer => customer.Id) %>
    First Name:
    <%= Html.TextBoxFor(customer => customer.FirstName) %>
    Last Name:
    <%= Html.TextBoxFor(customer => customer.LastName) %>
    <input type="submit" value="save" />
<% } %>

This is the html that would get generated:

<form action="/Customer/Save" method="post">
    <input id="Id" name="Id" type="hidden" value="9100eb3c-86fa-4d4d-a81b-b53532c48a06" />
    First Name:
    <input id="FirstName" name="FirstName" type="text" value="John" />
    Last Name:
    <input id="LastName" name="LastName" type="text" value="Doe" />
    <input type="submit" value="save" />
</form>

Saving a Customer - Exploring Controller Actions

We've created the view/html. Here is the controller action that would be needed to service the form above:

HttpPost
ActionName("Save")
public ActionResult Save(Guid id, string firstName, string lastName)
{
    //map the properties to a customer object so that 
    //it can be saved to the repository
    Customer customer = 
        new Customer 
        { 
            Id = id, 
            FirstName = firstName,
            LastName = lastName
        };

    //save customer to the database
    //redirect to edit screen after save
    return RedirectToAction("Edit", new { id = id }); 
}

The controller action is nice...but has some shortcomings:

The controller action is strongly typed (which is favorable), but I'm tasked with mapping the properties in the Customer object manually. It also has a fidelity issue...if I decided to refactor and change Id to CustomerId or LastName to Surname, I would have to change the parameters in the controller method respectively.

This controller action can also service the form:

HttpPost
ActionName("Save")
public ActionResult Save(FormCollection formCollection)
{
    Customer customer = new Customer();
    UpdateModel<Customer>(customer, formCollection);
     
    //save customer to the database
    //redirect to edit screen after save
    return RedirectToAction("Edit", new { id = customer.Id }); 
}

Again, it'll work, but I don't like that I'm taking in an arbitrary, loosely typed FormCollection (which smells bad to me). On top of this, I still have to map the properties using the UpdateModel() method...

The Better Controller Action

I prefer the following controller action:

HttpPost
ActionName("Save")
public ActionResult Save(Customer customer)
{
    //save customer to the database
    //redirect to edit screen after save
    return RedirectToAction("Edit", new { id = customer.Id }); 
}

By taking in a strongly typed Customer as a parameter, I can let ASP.NET MVC do all the mapping for me....did I mention it's also strongly typed? Woot. This is where TextBoxFor (and all the other For helpers for that matter) really shines. This method will render the html for binding the model to the controller action with no problems what-so-ever. You can even do more complicated bindings.

A More Complicated Binding

Lets say I have the following view model.

    public class CustomerContactViewModel
    {
        public Customer Customer { get; set; }
        public ContactPerson ContactPerson { get; set; }
    }

    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class ContactPerson
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

And I have the following View Page with a save customer/contact person form.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<CustomerContactViewModel>" %>

...

<% using(Html.BeginForm(new { controller = "Home", action = "Customer" })) { %>
       <!-- customer info -->
       <%= Html.HiddenFor(c => c.Customer.Id, null)%>
       Customer First Name:<br />
       <%= Html.TextBoxFor(c => c.Customer.FirstName, null)%><br />
       Customer Last Name:<br />
       <%= Html.TextBoxFor(c => c.Customer.LastName, null)%><br />
       
       <!-- customer contact person info -->
       <%= Html.HiddenFor(c => c.ContactPerson.Id, null)%>
       Customer Contact First Name:<br />
       <%= Html.TextBoxFor(c => c.ContactPerson.FirstName, null)%><br />
       Customer Contact Last Name:<br />
       <%= Html.TextBoxFor(c => c.ContactPerson.LastName, null)%><br />
       <input type="submit" value="save" />
<% } %>

This is the html that will get rendered:

<form action="/Home/Customer" method="post">
    <!-- customer info -->
    <input id="Customer_Id" name="Customer.Id" type="hidden" value="afeb1d9f-acc7-464a-9172-8d0fa53d1b3a" />
    Customer First Name:<br />
    <input id="Customer_FirstName" name="Customer.FirstName" type="text" value="" /><br />
    Customer Last Name:<br />
    <input id="Customer_LastName" name="Customer.LastName" type="text" value="" /><br />

    <!-- customer contact person info -->
    <input id="ContactPerson_Id" name="ContactPerson.Id" type="hidden" value="a5079b2c-355c-4b05-b6ae-91cd1b0a48f1" />
    Customer Contact First Name:<br />
    <input id="ContactPerson_FirstName" name="ContactPerson.FirstName" type="text" value="" /><br />
    Customer Contact Last Name:<br />
    <input id="ContactPerson_LastName" name="ContactPerson.LastName" type="text" value="" /><br />
    <input type="submit" value="save" />
</form>

The controller action below gets bound without a hitch!

HttpPost
ActionName("Save")
public ActionResult Save(Customer customer, ContactPerson contactPerson)
{
    //save customer to the database
    //save employee to the database
    //redirect to edit screen after save
    return RedirectToAction("Edit", new { id = customer.Id }); 
}

The Joke

I was searching for a good MVC2 logo to associate with this blog posts...so I Googled images for "mvc 2" and got the following result:

MVC 2....get it?


Written: 3/31/2010