3

Newbie ASP.NET MVC question:

I have the following model:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

And the following view for customer:

<% using (Html.BeginForm()) { %>
    First Name: <%=Html.TextBox("FirstName") %>
    Last Name: <%=Html.TextBox("LastName") %>
    <% Html.RenderPartial("AddressView", Model.Address); %>
    <input type="submit" name="btnSubmit" value="Submit"/>
<%} %>

And the following partial view for Address:

<%=Html.DropDownList("CountryId", new SelectList(Country.GetAll(), "Id", "Name") })%>
<%=Html.DropDownList("CountrySubdivisionId", new SelectList(CountrySubDivision.GetByCountryId(Model.CountryId), "Id", "Name"))%>

And the following controller action:

    [AcceptVerbs(HttpVerbs.Post)]
    public ViewResult Index(Customer customer, Address address)
    {
        customer.Address = address;
        ViewData.Model = customer;
        return View();
    }

I was hoping that the action would work with 1 parameter: customer, and that I would not have to reassign customer.Address manually. However, when the action is executed, Customer.Address is null.

Am I using model binding incorrectly, or does my action require separate parameters for Customer and Address?

Josh Kodroff
  • 27,301
  • 27
  • 95
  • 148

3 Answers3

2

It should be binded to customer, as customer defines an Address Property typeof(Address). your address Partial View should define the names like

//Here the Model refers to Model.Address in the PartialView
<%=Html.TextBox("Address.property1", Model.property1) %>

That way the ModelBinder knows that the address properties should be binded to the Address property part of the customer object.

EDIT: Add Address to the elements names:

<%=Html.DropDownList("Address.CountryId", new SelectList(Country.GetAll(), "Id", "Name") })%>
<%=Html.DropDownList("Address.CountrySubdivisionId", new SelectList(CountrySubDivision.GetByCountryId(Model.CountryId), "Id", "Name"))%>
JOBG
  • 4,544
  • 4
  • 26
  • 47
  • What if my Customer object has 2 addresses: ShippingAddress and BillingAddress? Wouldn't I need to assign each address in the action? – Josh Kodroff Dec 16 '09 at 15:15
  • If ShippingAddress and BillingAddress has their respective definition as part of the Customer Model you don't need to assign it manually as the modelbinder will do this for you, if the names are correctly defined in the View. – JOBG Dec 16 '09 at 15:20
  • Right, but I obviously don't want to define my control names as "ShippingAddress.CountryId" in the partial view because then it wouldn't work for BillingAddress. I may make this a whole separate question if I can't figure it out. I'll post the link here. – Josh Kodroff Dec 16 '09 at 16:36
  • I'm not sure what you intent to do, because i don't know the complete structure of the Customer Model. From what you wrote i think that you want to share properties that are common on both ShippingAddress and BillingAddress. If that is what you intent then this could be accomplished by setting getters and setters in a view model. – JOBG Dec 16 '09 at 17:51
2

The POST action has no knowledge of the view at all. It doesn't know or care that a partial view was involved.

The only thing that it sees is the POSTed HTML form. You can see this in Firebug or Fiddler. So you can have only one Customer argument to the POST action if the form has the right key names and values.

There are a lot of rules about this, but the answer to your question is that the fact that you used a partial view has no effect whatsoever on model binding to the POST. The only thing that matters is the contents of the form.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
2

Use Html.EditorFor instead of Html.RenderPartial.

See Model binding with nested child models and PartialViews in ASP.NET MVC.

Community
  • 1
  • 1
Pavel Chuchuva
  • 22,633
  • 10
  • 99
  • 115