EditorFor not rendering enumerable without #foreach - asp.net-mvc

This is rather baffling. Imagine a Company entity with an .Employees enumerable property, for the sake of simplicity. I've seen elsewhere on SO that the trick to getting EditorTemplates to render enumerables sensibly is to use syntax like this:
In Index.cshtml:
#Html.EditorFor(company => company.Employees, "EmployeeEditorTemplate")
EmployeeEditorTemplate.cshtml expects a single employee:
#model MyNamespace.Models.Employee
//...
#Html.EditorFor(x => x.FullName)
// ...etc.
Posts like this one: Viewmodels List Is Null in Action ...indicate that the modelbinder is smart enough to figure out the binding and index the enumerable on the fly. I'm getting different results (exactly what I'd expect if the modelbinder wasn't supposed to be so mageriffic):
The model item passed into the dictionary is of type
System.Collections.Generic.List`1[MyNamespace.Models.Employee]',
but this dictionary requires a model item of type
'MyNamespace.Models.Employee'.
Enumerating it myself renders the collection just fine, but emits the same id for every row's controls (which is less than optimal, of course, but it tells me the EditorTemplate is functionally correct):
#foreach (var item in Model.Employees)
{
#Html.EditorFor(x => item, "EmployeeEditorTemplate")
}
This construct also fails to post the company.Employees collection back to my other controller action.
Any ideas on what's required to achieve the following?
Render the child collection with unique Ids per instance in the list
Post the child collection back with the model
I don't care if I have to enumerate it myself, nor whether it's inside or outside the EditorTemplate; I've just seen it said that this is unnecessary and that MVC works better if you let it handle things. Thanks.

Per #David-Tansey (from this answer - see for a much more detailed explanation):
(changed to suit this question):
When you use #Html.EditorFor(c => c.Employees) MVC convention chooses the default template for IEnumerable. This template is part of the MVC framework, and what it does is generate Html.EditorFor() for each item in the enumeration. That template then generates the appropriate editor template for each item in the list individually - in your case they're all instances of Employee, so, the Employee template is used for each item.
To sum up his explanation on when you use the named template, you are, in effect, passing the entire IEnumerable<Employee> to a template expecting a single Employee by using #Html.EditorFor(company => company.Employees, "EmployeeEditorTemplate").
note: I really only answered this because it had no answer and I stumbled here first. At least now someone doesn't close it and potentially miss an answer without having to go through the rigmarole of getting duplicate question votes, blah blah.

You can read about it here: Hanselman's article
In short, you should write in your view (index.cshtml):
int i=0;
#foreach (var item in Model.Employees)
{
#Html.TextBox(string.Format("Model[{0}].FullName", i), item.FullName)
i++;
}

foreach can be used with #HTML.EditorFor, for an example see below syntax:
#foreach(var emp in #Model)
{
#Html.EditorFor(d => emp.EmployeeName)
#Html.EditorFor(d => emp.Designation)
<br />
}

Related

MVC Get and Post data

Sorry for the really nooby question, but this is tripping me up hard.
I have a controller Inspection, with method Configure which passes a model containing 3 lists.
I know that I'm supposed to use HiddenFor to retain the information, but I don't know how to do this with lists.
I have these HiddenFor fields but they don't seem to work in retaining the information
#Html.HiddenFor(model => Model.inspection)
#Html.HiddenFor(model => Model.assignedParts)
for (int i = 0; i < Model.ConfigList.Count; i++)
{
#Html.HiddenFor(model => Model.ConfigList[i])
}
Every Razor directive gets converted into html to display in the browser. In this case, your directive for Model.assignedParts will probably look something like this in html:
<input type = "text" value="List<StevenHubs.Models.Part>" />
You can see exactly what it is by running your app and viewing the source. The text that is getting assigned to value is just Model.assignedParts.ToString(). The simplest way to fix your problem is to change your view to just use the pieces of the model that are actually getting changed.
If you want to include the list in your View, you will need to do a foreach over the list, and if the member of the list has properties, you will have to have a #Html.HiddenFor for each property so something like:
#foreach(var part in Model.Part) {
#Html.HiddenFor(part.Property1)
#Html.HiddenFor(part.Property2)
}

Passing different models to RenderPartial()

I am chaining some partial views together, and I require a way to use different models in my call to RenderPartial(). Most answers on SO state to cast to the model type, but this will not be known at compile-time.
The website has different categories such as Cars, Planes, Helicopters and Boats. When the user clicks on say the Cars link, the Car controller's Index page displays all cars in a pretty table. Same for Planes etc.
All the tables are identical, so I want to use a partial view to reuse the code.
#model IEnumerable<MyNamespace.Entities.Car>
#{Html.RenderPartial("Partials/_TableList", Model);}
Inside _TableList.cshtml is my pretty table. So I now want to call another partial view, which takes a list of models of type Car, and output the table head and body. Likewise if the user had clicked Planes, this would load a partial that takes a list of Plane models.
<table class="table prettyTable">
#{Html.RenderPartial((string)ViewBag.PartialToLoad, Model);}
</table>
And in _CarList.cshtml
#model IEnumerable<MyNamespace.Entities.Car>
<thead>...</thead>
<tbody>
#foreach (var item in Model)
{ ... }
</tbody>
Can this be done? Or must I put the model in the ViewBag and pass that around?
EDIT: Thanks to #Adas, I was able to solve this by adding
#model IEnumerable<dynamic>
to the top of _TableList.cshtml. Now, when I call RenderPartial() it does not complain that "Extension methods cannot be dynamically dispatched". This now works perfectly:
<table class="table prettyTable">
#{Html.RenderPartial((string)ViewBag.PartialToLoad, Model);}
</table>
FURTHER EDIT: I found that although the above worked with IEnumerable it did not with #model dynamic.
I discovered that one can call RenderPartial this way, which works in this instance.
#{RenderPartialExtensions.RenderPartial(Html, (string)ViewBag.PartialToLoad, Model);}
where PartialToLoad in the ViewBag is set in the controller.
You have 3 options here:
use dynamic model, i.e. #model IEnumerable<dynamic>
Implement some common interface on your models and use it #model IEnumerable<IYourCommonInterface>
Inherit all your models from some common class, like emre nevayeshirazi commented, #model IEnumerable<YourBaseClass>.
It does not matter will you pass your model like Model, or you put it in the ViewBag, you will need one of the above methods to access common methods.

DisplayFor and EditorFor Helpers seem to be showing different Id values?

I have a really weird one.
I have a master - detail relationship, lets call it Order and OrderItem. On the front page I have a list of Orders and alongside each row I have an "Edit" link which takes me to an edit page.I need to show the Order details at the top and I edit the Order Item details at the bottom.
However the "Id" hidden field for the "Order Item" form is showing the "Order" Id Value. What is even more weird is that DisplayFor shows the correct value and HiddenFor and EditorFor Show the "Order" Id Value.
I am using a View Model to contain the Master and Detail entities for the Razor view which I populate in the controller.
Assume Order# = 16 and OrderItem# = 7
So :
#model vmOrder // ViewModel that contains myOrder and MyOrderItems.
#Html.EditorFor(m=>m.myOrderItems.First().Id) // Produce 16
#Html.HiddenFor(m=>m.myOrderItems.First().Id) // Produce 16
#Html.DisplayFor(m=>m.myOrderItems.First().Id) // Produce 7 which is correct.
I did intend to just return the first record hence "First()" for other reasons. It seems MVC is getting muddled...
Any thoughts. Seems most bizarre to me?
Many thanks,
Sam
What is myOrderItems? If it contains the results of a deferred LINQ query, each time you call First it will re-evaluate the query. Depending on how you have your query written, this could cause the value to change.
If myOrderItems is IEnumerable, make sure you are calling ToList or ToArray to force the query to run.
Second, don't call First multiple times. Try something like this.
#model vmOrder // ViewModel that contains myOrder and MyOrderItems.
#var first = Model.myOrderItems.First()
#Html.EditorFor(m => first.Id)
#Html.HiddenFor(m => first.Id)
#Html.DisplayFor(m => first.Id)

How to access model property in Razor view of IEnumerable Type?

How to access Model property(Like #Html.EditorFor(x=>Model.Name)) in Razor view of IEnumerable Type without using Loops??I.e If a view is strongly typed to some Model holding Model as a LIST.Eg.
#model IEnumerable<EFTest2.DAL.package_master>
Then is it possible to Display TestBoxFor or EditorFor (to create new Model) Html helper without using foreach Loop.???
When some model property is of type IEnumerable<SomeType> you would normally define an editor/display template (~/Views/Shared/EditorTemplates/SomeType.cshtml or ~/Views/Shared/DisplayTemplates/SomeType.cshtml). This template will be automatically rendered for each element of the collection so that you don't need to write loops:
#Html.EditorFor(x => x.SomeCollection)
and inside the template you will be able to access individual properties:
#model SomeType
#Html.EditorFor(x => x.Name)
...
Now, if you absolutely need to directly access some element inside a view which is strongly typed to IEnumerable<SomeType> you would better use some other collection type such as IList<SomeType> or SomeType[] as view model which will give you direct access to elements by index and you will be able to do this for example to access the 6th element of the collection:
#model IList<SomeType>
#Html.EditorFor(x => x[5].Name)
So basically you are mentioning of type List etc.
If you want to add values to the list accepting input from user from form fields and then add to the existing list in model. the easiest but not so elegant way would be
suppose you have class "Person" and then List in your model. first create an instance of person that will have empty person instance , add it to list and then bind this last list item to your edit for.
#{
Person contact = new Person(); //Empty person instance
PersonList.Add(contact);
}
later on bind this to your induvidual fields
#Html.TextBoxFor(m => m.PersonList[PersonList.Count - 1].PrimaryContacts.FirstName)
This works good for Validation on model properties as-well and also after every form submit, for next inclusion you just add the new person. but take care to write the remove peice of code if form is cancelled.

Using multiple partial views in MVC3 razor forms

I have an insurance entry form that has contact information for two people. I have created a Razor partial view for the contact entry and put it in the form twice. The 'master' view model (VmApplicationForm) contains two instances of a subsidiary view model (VmPolicyHolder) corresponding to the two contacts as well as some properties common to both contacts. I am calling #Html.RenderPartial("_CreateOrEdit", Model.contactInfo1) and #Html.RenderPartial("_CreateOrEdit", Model.contactInfo2) in the page. With this arrangement (no surprises) the rendered code has duplicate IDs for the form input elements.
Is there any way of getting RenderPartial to prefix the IDs and Name attributes? I couldn't see this in the documentation, but perhaps I've missed something.
Sorry, I don't have time yet to give you the example code, but i'll give you the idea. You should first create EditorTemplate for that probably called ContactInfo class. And then, in the base class(Holding that two contacts) edit view, you should write
#Html.EditorFor(model => model.contactInfo1)
#Html.EditorFor(model => model.contactInfo2)
This way, it will render that EditorTemplate and generate correct ids and names to inputs within it.
What you are doing is trying to post a collection of items in a form- this can indeed be done in MVC (as well as in any web page/application that uses a FORM tag), however it requires some special handling to avoid id collisions and to correctly format the post data. Steve Sanderson has a great post on how to accomplish this:
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Essentially its a wrapper to append unique guids to element ids (in your case for each contactInfo), and create the proper array format in the tags. eg <input name="ContactInfo[f2cc4d6b-fc32-45e9-9d3d-fce54c3fede3].FirstName">
if your model is something like ContactInfo, you will end up posting like
[HttpPost]
public ActionResult Index(IEnumerable<ContactInfo> contacts)
{
// To do: do whatever you want with the data
}

Resources