asp.net-mvcpartial-viewsmodel-bindingcustom-model-binderstrongly-typed-view

A Strongly Types Partial View which is dynamically model binded at runtime to 2 different model class


There is main RegisterModel calling nested HomeAddress and MailAddress model.

public class RegisterModel
{
   Public string FirstName {get; set;}
   Public string LastName  {get; set;}
   Public HomeAddressModel homeAddress {get; set;}
   Public MailAddressModel mailAddress {get; set;}
}

public class HomeAddressModel
{
    Public string Street1 {get; set;}
    Public string Street2  {get; set;}
    Public string State {get; set;}
    Public string City {get; set;}
}

public class MailAddressModel
{
    Public string Street1 {get; set;}
    Public string Street2  {get; set;}
    Public string State {get; set;}
    Public string City {get; set;}
}

The Partial View for Address

@model MyNamespace.Models.???
@{
    Layout = "~/Views/_Layout.cshtml";
}
 <div id="Address">
   //Street1
   //Street2
   //State
   //City
 </div>

How will i define my Parital view so that I can bind it at runtime either with HomeAddressModel or MailAddressModel.

My main Register View

@model MyNamespace.Models.RegisterModel
@{
     Layout = "~/Views/_Layout.cshtml";
 }
@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
  <div id="form">
    @Html.TextBoxFor("FirstName");
    @Html.TextBoxFor("LastName");
   //Render Partial View for HomeAddress.
   //Will provide a checkbox if Mailing Address is different.
   //Render Partial View for MailAddress.
  </div>
}

public ActionResult Register()
{  
    var model = new RegsiterModel();
    return View(model);
}

[HttpPost]
public ActionResult Register(RegisterModel model, 
                            HomeAddressModel  homeAddress, 
                            MailAddressModel mailingAddress)
{
       //Do Something with different Addresses
       return View();
}

There are 5 parts to this question : -

  1. Is the RegisterModel Class created correctly ? This is they way we can nest them ?
  2. Should we have single class for Address and 2 different Properties for both Addresses ? somethign like Address home {get;set;} and Address mail{get;set;}. if yes then how to achieve next things.
  3. How to create partial view for Address ? in both scenarios that is using separate HomeAddres class and MailAddress class. OR using single Address class.
  4. How to declare partialView in Main Register View with both approach as described above.
  5. How to make sure that in [HttpPost] Action Method we can read all values i.e. RegisterModel Values is binded, and individual addresses are also binded.

Solution

  • Have a single model for address like

    public class AddressModel
    {
        Public string Street1 {get; set;}
        Public string Street2  {get; set;}
        Public string State {get; set;}
        Public string City {get; set;}
    }
    

    Make a partial view for it in Views/Shared/EditorTemplates/AddressModel.cshtml

    @model MyNamespace.Models.AddressModel
    <div id="Address">
       Html.TextBoxFor(m => m.Street1)
       //Street2
       //State
       //City
    </div>
    

    Now if you have your view model

    public class RegisterModel
    {
       Public string FirstName {get; set;}
       Public string LastName  {get; set;}
       Public AddressModel HomeAddress {get; set;}
       Public AddressModel MailAddress {get; set;}
    }
    

    Simply render partial views for each address like this

    <div id="form">
        @Html.TextBoxFor(m => m.FirstName);
        @Html.TextBoxFor(m => m.LastName);
        @Html.EditorFor(m => m.HomeAddress) // !! -> this will render AdressModel.cshtml as a partial view, and will pass HomeAdress to it
       //Will provide a checkbox if Mailing Address is different.
        @Html.EditorFor(m => m.MailAddress)
    </div>
    

    Optionally you can wrap the call to EditorFor to your own helper method if you need more logic (additional parameters for the view or something like that)

    In HttpPost method use this

    [HttpPost]
    public ActionResult Register(RegisterModel model)
    {
    }
    

    and the addresses will bind to properties of RegisterModel.