I'm new to .NET all together, please be patient with me if I have any silly mistakes.
I'm using ASP.NET MVC 3 with .NET 4.0
I want to have a "Create" view for a model that has a child Model. This view should include the child model's partial "Create" view, I'll use the following simple example for illustration purposes:
The Person model
class Person { public string Name { get; set; } public Address { get; set; } }
The Address model
class Address { public string City { get; set; } public string Zip { get; set; } //A List for creating a <select/> item in the view //containing cities fetched from the database. //The initialization is done in the controller action returning //the related partial view. public IEnumerable<SelectListItem> CityDropDown { get; set; } ) }
The Controller Actions
class MyController : Controller { public ViewResult Create() { var person = new Person(); var address = new Address(); // initialization of address.CityDropDown omitted person.Address = address; return View(MyViews.CreatePersonView, person); } [HttpPost] public ViewResult Create(Person person) { //persistance logic } }
The views hierarchy I want to have :
The solutions that I have tried in order to achieve this are the following :
First approach : Using @Html.Partial(..)
or @{Html.RenderPartial(..)}
What I did :
The Person view
@model Person @using(Html.BeginForm()){ @Html.EditorFor(m=>m.Name) @Html.Partial(MyViews.AddressPartialView, @Model.Address) }
The Address partial view
@model Address @Html.EditorFor(m=>m.Zip) @Html.DropDownListFor(m=>m.City, @Model.CityDropDown)
The problem :
When submitting the form, person.Address
is null. After a bit of searching on Google, I found out that in order for the submit of the address field to work, the generated HTML markup must be the following (notice the Address_
prefix) :
<form...>
<input type=text id="Name" />
<input type=text id="Address_Zip" />
<select id="Address_City">
<!-- options... -->
</select>
</form>
Needless to say, the generated HTML markup in my case isn't the same but instead it's the following (the Address_
prefix is missing) :
<form...>
<input type=text id="Name" />
<input type=text id="Zip" />
<select id="City">
<!-- options... -->
</select>
</form>
Second approach : Using an EditorTemplate for the Address model
What I did :
I moved the Address partial view to the folder View/Shared/EditorTemplates assuring that it has the same name as the
Address
property in thePerson
model, i.e Address.cshtml.The Person view
@model Person @using(Html.BeginForm()){ @Html.EditorFor(m=>m.Name) @Html.EditorFor(@Model.Address) //will automatically find the Address //partial view in the EditorTemplates folder }
The problem :
Using this approach the generated markup has in fact the proper prefix (i.e. Address_
), but I get an Object reference not set to an instance exception for the Address.CityDropDown
property which tells me that the pre-initialised Address object in the controller's action isn't passed to the partial view for some reason.
Third approach : put all Address fields in the Person model
This approach works with no problems, but I don't want to use it as I don't want to have redundant code if I ever want to have a create view for address in another model.
To sum up
What should I do in order to have a reusable partial create view that I can use accross my application?
You had the correct approach with EditorTemplates, but keep in mind you need to populate the
CityDropDown
. So, the view should be handed off something like:Which would then make this view only consist of:
And then your
EditorTemplates
would pick up from there:~/Views/shared/EditorTemplates/Address.cshtml (Note: this is based on type not property name)
~/Views/Shared/EditorTemplates/Person.cshtml
the three views then render something like:
Example project can be found here: https://github.com/bchristie/StackOverflow-Examples/tree/master/questions-19247958