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 :
@Html.Partial(..)
or @{Html.RenderPartial(..)}
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)
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>
I moved the Address partial view to the folder View/Shared/EditorTemplates assuring that it has the same name as the Address
property in the Person
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
}
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.
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.
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:
Person model = new Person()
{
Address = new Address
{
CityDropDown = new SelectListItem[]{
new SelectListItem { Selected = true, Text = "Select one..." },
new SelectListItem { Text = "Anywhere", Value = "Anywhere" },
new SelectListItem { Text = "Somewhere", Value = "Somewhere" },
new SelectListItem { Text = "Nowhere", Value = "Nowhere" }
}
}
};
Which would then make this view only consist of:
@Html.EditorForModel()
And then your EditorTemplates
would pick up from there:
~/Views/shared/EditorTemplates/Address.cshtml (Note: this is based on type not property name)
@model MvcApplication.Models.Address
@Html.DropDownListFor(x => x.City, Model.CityDropDown)
@Html.EditorFor(x => x.Zip)
~/Views/Shared/EditorTemplates/Person.cshtml
@model MvcApplication.Models.Person
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Name)
@Html.EditorFor(x => x.Address)
<input type="submit" value="Save" />
}
the three views then render something like:
<form action="/" method="post">
<input class="text-box single-line" id="Name" name="Name" type="text" value="" />
<select id="Address_City" name="Address.City">
<option selected="selected">Select one...</option>
<option value="Anywhere">Anywhere</option>
<option value="Somewhere">Somewhere</option>
<option value="Nowhere">Nowhere</option>
</select>
<input class="text-box single-line" id="Address_Zip" name="Address.Zip" type="text" value="" />
<input type="submit" value="Save" />
Example project can be found here: https://github.com/bchristie/StackOverflow-Examples/tree/master/questions-19247958
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With