I have a ViewModel which contains a child ViewModel. In a strongly typed Viewed of the parent, I want to RenderPartial on the child, and have results persist following POST.
But the child fields are always null.
This should work, shouldn't it? I am fairly new to MVC, hopefully I'm missing something simple. Hopefully someone can point it out!
Thanks!
Example
ViewModels
public class EggBoxViewModel
{
public string Brand { get; set; }
public int Price { get; set; }
public EggViewModel Egg { get; set; }
}
public class EggViewModel
{
public string Size { get; set; }
public bool IsBroken { get; set; }
}
Controller
public ActionResult Demo()
{
EggBoxViewModel eggBox = new EggBoxViewModel();
eggBox.Brand = "HappyEggs";
eggBox.Price = 3;
EggViewModel egg = new EggViewModel();
egg.Size = "Large";
egg.IsBroken = false;
eggBox.Egg = egg;
return View(eggBox);
}
[HttpPost]
public ActionResult Demo(EggBoxViewModel eggBox)
{
// here, eggBox.Egg is null
}
Views
"Demo"
#model MvcApplication1.ViewModels.EggBoxViewModel
#using (Html.BeginForm())
{
<h2>EggBox:</h2>
<p>
#Html.LabelFor(model => model.Brand)
#Html.EditorFor(model => model.Brand)
</p>
<p>
#Html.LabelFor(model => model.Price)
#Html.EditorFor(model => model.Price)
</p>
<p>
#{Html.RenderPartial("_Egg", Model.Egg);}
</p>
<input type="submit" value="Submit" />
}
"_Egg" (Partial)
#model MvcApplication1.ViewModels.EggViewModel
<h2>Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.EditorFor(model => model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBoxFor(model => model.IsBroken)
</p>
Use an Editor Template, in most cases they're better for rendering child objects or collections. In your Views\Shared folder create a new folder called EditorTemplates if you don't already have one. Add a new partial view called EggViewModel and use the same code as in your partial view. This is the bit of magic that renders and names all your fields correctly. It will also handle a collection of EggViewModels without a for each loop as the Editor template will automatically render all the items passed in a collection:
#model MvcApplication1.ViewModels.EggViewModel
<h2>Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.EditorFor(model => model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBoxFor(model => model.IsBroken)
</p>
Then in your Demo View use your new Editor Template instead of the partial:
<p>
#Html.EditorFor(x => x.Egg)
</p>
Here are the fields that are rendered:
In the post back you can that the EggViewModel is now part of the EggBoxViewModel:
Also in the ModelState you can see that the Egg fields are prefixed with the property name used in the EggBoxViewModel which makes them a subset of EggBox.
And...the great thing is that if you want to have a collection of Eggs in your EggBox it's really easy...just make your Egg property on EggBoxViewModel a collection:
public class EggBoxViewModel
{
public string Brand { get; set; }
public int Price { get; set; }
public ICollection<EggViewModel> Eggs { get; set; }
}
Add a second egg:
public ActionResult Demo()
{
EggBoxViewModel eggBox = new EggBoxViewModel();
eggBox.Brand = "HappyEggs";
eggBox.Price = 3;
EggViewModel egg = new EggViewModel();
egg.Size = "Large";
egg.IsBroken = false;
EggViewModel egg2 = new EggViewModel();
egg2.Size = "Medium";
egg2.IsBroken = false;
eggBox.Eggs = new List<EggViewModel>();
eggBox.Eggs.Add(egg);
eggBox.Eggs.Add(egg2);
return View(eggBox);
}
Change your View to render x.Eggs instead of x.Egg:
<p>
#Html.EditorFor(x => x.Eggs)
</p>
Then on post back you'll see there are 2 Eggs posted back:
The field names have automatically been indexed and named to create a collection of Eggs:
you'll have to change your partial view model to EggBoxViewModel so that the Egg object is exposed on the view. i.e:
#model MvcApplication1.ViewModels.EggBoxViewModel
<h2>
Egg</h2>
<p>
#Html.LabelFor(model => model.Egg.Size)
#Html.EditorFor(model => model.Egg.Size)
</p>
<p>
#Html.LabelFor(model => model.Egg.IsBroken)
#Html.CheckBoxFor(model => model.Egg.IsBroken)
</p>
thus, you'd call it from the main view as:
#{Html.RenderPartial("_Egg", Model);}
you should now see the Egg object in your model being returned in the HttpPost action.
[Update method 2]
ok, really quick edit!! You can keep everything the same as initially and try this in your partial (i'm not 100% certain that this will work tho):
#model MvcApplication1.ViewModels.EggViewModel
<h2>
Egg</h2>
<p>
#Html.LabelFor(model => model.Size)
#Html.Editor("Egg.Size", Model.Size)
</p>
<p>
#Html.LabelFor(model => model.IsBroken)
#Html.CheckBox("Egg.IsBroken", Model.IsBroken)
</p>
here, I just use #Html.Editor() and input the required model name, rather than #Html.EditorFor(). Ok, gorra dash... train to 'cache' ;-)
Related
StudentModel.
namespace mvcApp.Models
{
public class StudentModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Email Address")]
public string EmailAddress { get; set; }
public List<SchoolOrganization> Organizations { get; set; }
}
public class SchoolOrganization
{
public string Name { get; set; }
public bool IsInvolved { get; set; }
}
}
Student is involved in multiple organizations.
Controller
namespace mvcApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult StudentInformation()
{
// populate student with data
var student = new StudentModel() { FirstName = "Joe", LastName = "Doe", EmailAddress = "jdoe#hotmail.com"};
// Populate with Organizations
student.Organizations = new List<SchoolOrganization>();
student.Organizations.Add(new SchoolOrganization() { Name = "Math Club", IsInvolved = true});
student.Organizations.Add(new SchoolOrganization() { Name = "Chess Club", IsInvolved = false });
student.Organizations.Add(new SchoolOrganization() { Name = "Football", IsInvolved = true });
return View(student);
}
**[HttpPost]
public ActionResult StudentInformation(StudentModel student)
{
Response.Write("Name: " + student.FirstName);
foreach (var o in student.Organizations)
{
Response.Write(o.Name + " : " + o.IsInvolved.ToString());
}
return View();
}**
}
}
Data will be eventually populated from database.
View
#model mvcApp.Models.StudentModel
#{
ViewBag.Title = "StudentInformation";
}
<h2>StudentInformation</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>StudentModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div>
<table>
<tr>
<td>Organization Name</td><td>Is Involved</td>
</tr>
#for (int i = 0; i < Model.Organizations.Count; i++) <== System.NullReferenceException here
{
#Html.HiddenFor(m => m.Organizations[i].IsInvolved)
<tr>
<td>#Html.DisplayFor(m => m.Organizations[i].Name)</td>
<td>#Html.CheckBoxFor(m => m.Organizations[i].IsInvolved)</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The above code displays fine with HttGet. However, when i try to update i get System.NullReferenceException. https://www.dropbox.com/s/dz0bg3hkd0yq8e3/studentInformation.png?dl=0
Can anyone please help figure it what's going on?
Thank you.
In the code sample you have provided; the HomeController's [HttpPost] ActionResult for StudentInformation does not create and pass a new instance of the updated object model to the view, but instead runs a basic debug.writeline routine. As a result the dependent view for the [HttpPost] view of "StudentInformation.cshtml", does not receive a populated instance of the updated model...
Setting a breakpoint on the first line of the model for the "StudentInformation.cshtml" page and running the application will demonstrate that the model referenced at the top of the page, has no data within it...
This is why the [HttpPost] version of the page simply renders the blank model, without any altered data values you may have created, UNTIL it gets to the section where the view is dependent on a count of new data values which must be present within the model that is called at first line of the page... to continue.
An empty data model set results in a null reference because there are no values to count within it.
In order to view an updated group of settings, The [HttpPost] version of the view model must be passed an instance of the model that returns the new information as in "return View(nameOfViewDataSet)" (you build a data set again, and pass it as a new version of the model, with revised form data present).
Until there is data passed via the return View statement relative to the [HttpPost] version of the StudentInformation ActionResult, to the actual view, there will be no display data, and the count function will continue to return a null value.
I hope that is helpful.
I have Model class "Customer". One of its properties is a collection of object lets say "Order". I want that during edit I can modify the list of orders associated with the selected Customer that I want to update but when I submit the modified customer and modified orders the order object is null. Please help how can I send the modified orders on edit. Here's my code
Class
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string OrderName { get; set; }
}
Edit View
#model MVCTestApp.Models.Customer
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Customer</legend>
#Html.HiddenFor(model => model.CustomerId)
<div class="editor-label">
#Html.LabelFor(model => model.CustomerName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CustomerName)
#Html.ValidationMessageFor(model => model.CustomerName)
</div>
#foreach (var order in Model.Orders)
{
<div class="editor-label">
#Html.Label(order.OrderName)
</div>
<div class="editor-field">
#Html.Editor(order.OrderName, new { id = order.OrderId })
</div>
}
<p><input type="submit" value="Save" /> </p>
</fieldset>
}
Submitted Edited View
Use a for-loop instead of foreach:
#for (var i=0;i<Model.Orders.Count();i++)
{
<div class="editor-label">
#Html.Label(Model.Orders[i].OrderName)
</div>
<div class="editor-field">
#Html.EditorFor(m=> Model.Orders[i].OrderName)
</div>
}
The reason for this issue can be easily seen on the rendered html of the foreach approach. The names of the elements do not have an index and they all have the same name. The model binder cannot infer how to pass it to your controller. Using a for-loop you are somewhat forcing (if that's the right term) the markup/html to have an index on your Orders objects. Having done that, the model binder can now properly map your inputs to the Orders field.
Oh by the way. Now that you are using a for-loop you need to verify that your Orders collection is not null. But I'm sure you can easily do that.
I'd recommend you to get orders in action like this:(check if order_OrderName is the name of the input filed in form, see the source for example with firebug or something)
public ActionResult Edit(int id, string[] order_OrderName , Customer customer )
{
//////
}
I can't seem to figure out how to validate the pieces of a partial view for an ViewModel that has the partial ViewModel as a child object. Here's my lowest level piece, which will ALWAYS be consumed as a partial view inside other form tags:
namespace MVC3App.ViewModels
{
public class Payment : IValidatableObject
{
public decimal Amount { get; set; }
public int CreditCardNumber { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Amount < 20)
yield return new ValidationResult("Please pay more than $20", new string[] { "Amount" });
}
}
}
And here's the 'main' ViewModel that includes it:
namespace MVC3App.ViewModels
{
public class NewCustomerWithPayment :IValidatableObject
{
public string Name { get; set; }
public int Age { get; set; }
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
}
}
}
For the View of the NewCustomerWithPayment, I have this:
#model MVC3App.ViewModels.NewCustomerWithPayment
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>NewCustomerWithPayment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</fieldset>
#Html.Partial("Payment")
<p><input type="submit" value="Create" /></p>
}
And the Partial View "Payment" is ALWAYS rendered inside another Html.Beginform tag, it just has this:
#model MVC3App.ViewModels.Payment
<h2>Payment</h2>
<fieldset>
<legend>Payment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreditCardNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreditCardNumber)
#Html.ValidationMessageFor(model => model.CreditCardNumber)
</div>
</fieldset>
My problem is that I cannot get the Validation on the 'Payment' viewmodel to work. Can anyone with experience using IValidatableObject on ViewModels which are rendered as Partial Views chime in and give me a validation pattern that works? I can live without JavaScript validation if I have to.
These answers all have some great info, but my immediate issue was resolved by using this:
#Html.EditorFor(model => model.PaymentInfo)
Instead of this:
Html.Partial("Payment", Model.PaymentInfo)
I was surprised that this worked, but it does. The EditorFor helper renders out the partial view just like Html.Partial, and wires in the validation automatically. For some reason, it does call the validation twice on the child model (Payment in my example), which seems to be a reported issue for some other people (http://mvcextensions.codeplex.com/workitem/10), so I have to include a boolean for 'HasBeenValidated' on each model and check for it at the beginning of the Validate call.
Update: you must move your view to the EditorTemplates folder under /Views/Shared/ in order for the view to be used by the EditorFor helper. Otherwise, the EditorFor will give you the default editing fields for the types.
Here is a lame example of a custom validator for a checkbox :) I would write a custom validator or use a regex maybe. This may get you on the right path and be easier.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CheckBoxMustBeTrueAttribute : ValidationAttribute, IClientValidatable
{
#region IClientValidatable Members
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredcheckbox"
};
}
#endregion
public override bool IsValid(object value)
{
if (value is bool)
{
return (bool) value;
}
return true;
}
}
Most probably IValidatableObject is recognized only on the root model. You can call the inner model Validate method from the root model:
public class NewCustomerWithPayment :IValidatableObject {
...
public ViewModels.Payment PaymentInfo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Age < 18)
yield return new ValidationResult("Too young.", new string[] { "Age" });
if (this.PaymentInfo != null)
yield return this.PaymentInfo.Validate(validationContext);
}
}
Note: Not sure if the above compiles.
I have a case where a complex partial view needs different validation of fields depending on where the partial view is used.
I thought I could get around this by making the partial view take an interface as the model type and implementing two different ViewModels based on the interface. The data annotations in the two ViewModels would be different. I would then supply an instance of the correct ViewModel to the partial view.
But what I'm finding is that the only annotations that are recognized are those on the interface itself. DAs on the interface-implementing ViewModel classes are ignored, even though those are the objects that are being passed as models. So my plan isn't working.
Is there a way around this? A better approach? I'd prefer not to split the partial view into separate views if I can avoid it.
ETA: This is an abbreviated version of the partial view, as requested:
#model IPerson
#Html.ValidationSummary(false)
<fieldset>
<table class="editForm">
<tr>
<td class="editor-label">
#Html.LabelFor(model => model.FirstName)
</td>
<td class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</td>
<td class="editor-label">
#Html.LabelFor(model => model.LastName)
</td>
<td class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</td>
</tr>
</table>
<fieldset>
The real partial view is quite long and has a lot of #if statements managing the rendering (or not) of optional sections, but it doesn't do anything tricky.
My idea isn't going to work: this thread reminded me that classes don't inherit attributes from their interfaces. (As the answer points out, what would happen if two interfaces specified the same property with different attributes, and both were implemented by one class?)
It might work with a common base class. I will try that tomorrow.
Thanks, everybody.
Ann, you're right. I've deleted my comment. You can't post an interface back through your view. However, I don't know what exactly your trying to do since I can't see your code. Maybe something like this? I'm passing an interface to the view, but passing it back as the class I'm expecting. Again, I'm not sure the application is here.
Let's say you have classes like this:
[MetadataType(typeof(PersonMetaData))]
public class Customer : IPerson {
public int ID { get; set; }
public string Name { get; set; }
[Display(Name = "Customer Name")]
public string CustomerName { get; set; }
}
public class Agent : IPerson {
public int ID { get; set; }
public string Name { get; set; }
}
public partial class PersonMetaData : IPerson {
[Required]
public int ID { get; set; }
[Required]
[Display(Name="Full Name")]
public string Name { get; set; }
}
public interface IPerson {
int ID { get; set; }
string Name { get; set; }
}
public interface IAgent {
int AgentType { get; set; }
}
public interface ICustomer {
int CustomerType { get; set; }
}
Your Controller looks like:
public ActionResult InterfaceView() {
IPerson person = new Customer {
ID = 1
};
return View(person);
}
[HttpPost]
public ActionResult InterfaceView(Customer person) {
if (ModelState.IsValid) {
TempData["message"] = string.Format("You posted back Customer Name {0} with an ID of {1} for the name: {2}", person.CustomerName, person.ID, person.Name);
}
return View();
}
And your View Looks like this:
#model DataTablesExample.Controllers.Customer
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#if (#TempData["message"] != null) {
<p>#TempData["message"]</p>
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>IPerson</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CustomerName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CustomerName)
#Html.ValidationMessageFor(model => model.CustomerName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Well, actually you have a very reasonable idea! and can be archived is you use the non generic version of the HtmlHelper methods (ex. "#Html.Editor" instead of "#Html.EditorFor"), because the generic versions recreate the ModelMetadata (i don't know why!) based on the generic parameter type and don't use the ModelMetadata of the view. Freaking awful, isn't it?
Hope this help.
I have a simplified test scenario useful for asking this question: A Product can have many Components, a Component can belong to many Products. EF generated the classes, I've slimmed them as follows:
public partial class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Component> Components { get; set; }
}
public partial class Component
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
The creation of a component is accomplished via these controller actions:
public ActionResult Create(int ProductId)
{
Product p = db.Products.Find(ProductId);
Component c = new Component();
c.Products.Add(p);
return PartialView(c);
}
[HttpPost]
public ActionResult Create(Component model)
{
db.Components.Add(model);
db.SaveChanges();
}
and the view returned by the GET method looks like this:
#model Test.Models.Product
<fieldset>
<legend>Product</legend>
<div class="display-label">Name</div>
<div class="display-field">#Model.Name</div>
</fieldset>
#Html.Action("Create", "Component", new {ProductId = Model.Id})
<p>
#Html.ActionLink("Edit", "Edit", new { id=Model.Id }) |
#Html.ActionLink("Back to List", "Index")
</p>
From which can be seen that the component creation is handled on the same page via the above Html.Action - the code for that view follows:
#model Test.Models.Component
#using Test.Models
<script type="text/javascript">
function Success() {
alert('ok');
}
function Failure() {
alert('err');
}
</script>
#using (Ajax.BeginForm("Create", "Component", new AjaxOptions
{
HttpMethod = "Post",
OnSuccess = "Success",
OnFailure = "Failure"
}))
{
<fieldset>
<legend>Components</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
#Html.HiddenFor(x => x.Products.First().Id)
#Html.HiddenFor(x => x.Products)
#foreach (Product p in Model.Products)
{
#Html.Hidden("Products[0].Id", p.Id)
}
#foreach (Product p in Model.Products)
{
#Html.Hidden("[0].Id", p.Id)
}
</fieldset>
<input type="submit" value="go" />
}
ok. so this is what I'm struggling with: I need the model parameter of the [HttpPost]back to get properly populated i.e. it should contain a Product, since I can't create the new component with a null product. To get the product I need to look it up via the product's id. I expect I should be able to do:
model.Products.Add(db.Products.Find(model.Products.First().Id));
or some such thing, which relies on model receiving the id. This means the view has to place the id there, presumably in a hidden field, and as can be seen from my view code, I've made several attempts at populating this, all of which have failed.
Normally I prefer the *For methods since they become responsible for generating correct nomenclature. If .Products were singular (.Product), I could reference it as x => x.Product.Id and everything would be fine, but since it's plural, I can't do x => x.Products.Id so I tried x => x.Products.First().Id which compiles and produces the right value but gets name Id (which is wrong since the model binder thinks it's Component.Id and not Component.Products[0].Id.
My second attempt was to let HiddenFor iterate (like I would with EditorFor):
#Html.HiddenFor(x => x.Products)
but that produces nothing - I've read that this helper doesn't iterate. I tried x => x.Products.First() but that doesn't even compile. Finally, I decided to abandon the *For and code the name myself:
#foreach (Product p in Model.Products)
{
#Html.Hidden("Products[0].Id", p.Id)
and though that looks right, the postback doesn't see my value (Products.Count == 0). I saw in some posting that format should look like [0].Id but that doesn't work either. grr...
I gather I could code it like this:
#Html.Hidden("ProductId", p.Id)
and then redeclare my controller action like this:
[HttpPost] ActionResult Create(Component model, int ProductId)
but that seems eecky. it's hard to believe this is so difficult. can anyone help?
e
p.s. I have a project I could make available for download if anyone cares
Instead of writing those foreach loops try using editor templates:
<fieldset>
<legend>Components</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
#Html.EditorFor(x => x.Products)
</fieldset>
and inside the corresponding editor template (~/Views/Shared/EditorTemplates/Product.cshtml)
#model Product
#Html.HiddenFor(x => x.Id)