Model binding a list of checkboxes problem - asp.net-mvc

In my EditorTemplates, i Have two Views. One for my Category (called _Category)
#model com.example.Models._Category
#Html.CheckBox(Model.Name, Model.Selected)
#Html.LabelFor(c => c.Name, Model.Name)
<br />
and one for the List of Categories (called _Categories)
#model List<com.example.Models._Category>
#for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(c => Model[i]);
}
In the view that shows these categories, i have a list of Categories which is being used like so:
#Html.EditorFor(m => m.Categories, "_Categories")
When I view the page, there are multiple checboxes with names next to them which is good. The name of the checkboxes is not so good however as they turn out to look like this:
....name="Categories.[1].Batman"....">
There is an extra dot in the name which needs to go away. Any ideas on how to fix this?
Thanks in advance

Please refer to this and this for collections databinding, those are two ultimate resources for that.

I saw this post that I believe talks about the same issue that you are having. Might be helpful.

Related

Getting children of a page by name

I'm trying to do something I thought was obvious, but I can't find a doc on this to save my life.. Basically, I want to build a list of links for all children of a page by it's name.. I'm trying this with no luck.. Is there an easy way to accomplish this?
#foreach (var page in Model.Content.Where(page => page.Name.Equals("About")).Children) {
...
}
If you mean you want links to the current page's children who are named "About" then you can do:
#foreach(var page in #Model.Content.Children.Where(x => x.Name == "About"))
{
#page.Name
}

How to access the Model from the View?

I have a search page that display a search result. The search result is a list of persons that matched the specific search. I'm iterating through this list displaying them in a table. As headers for this table I want the DisplayName from the model. If I don't inherit IEnumerable I wouldn't be able to iterate through the list. I'm new at this MVC thing =)
I iterate through the result like this:
<% foreach (var item in Person) { %>
<%: item.surname %>
<% } %>
But how do I print the "DisplayName" of an attribute without iterating through the whole list? I would just like to do:
<%: Html.LabelFor(m => m.surname) %>
If it's any help I inherit an IEnumerable at the top of the page:
<%# Page Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>" %>
Edit
I want to display "Your surname" but I don't know how to access it from the view.
[DisplayName("Your surname")]
public object surname { get; set; }
Here's a very similar question that hasn't been answered either: Can I use LabelFor() when the page inherits from IEnumerable<T>?
If you only need to display specifics of one person; you should consider sending only one person to the view instead of a complete list of persons. In that case
Model.Surname
would work just like that. So instead of:
Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>"
do
Inherits="System.Web.Mvc.ViewPage<RegistryTest.Models.Person>"
In this case a single person is loaded into your Model and Model.Property works fine. If you want an IENumerable<>, think about what that means. You are sending a list of persons, so the only thing in your "Model" is a IENumerable<> of persons. There is no way that the view can know what you want if you call Model.Property, since in the Model there are multiple Objects and the view doesn't know which Object you want to get the Property from.
Bottom line, if you want to call Model.Property (Model.surname) but also want to send an IENumerable you are having a design flaw. If you send a list you should want to do something with the complete list (iterate through and do something with the contents). If you just want to do something with one person in that list, re-design your view/controller and send that single person; then you can use Model.Property.
//EDIT BASED UPON COMMENTS
As I see it now you either want to do one of those two things (I do not know which):
Show the records of an item in your list in a table and put the DisplayName of the current object shown in the table in the header.
Show all items of the list in your table and put some sort of DisplayName in the header. This makes less sence but it could be that you mean to name your list.
Situation 1
This is working as the rest of your code? The following would work just fine.
<% foreach (var item in Model) { %>
<table>
<th>item.DisplayName</th>
<tr>
<td>item.Property1</td>
<td>item.Property2</td>
</tr>
</table>
<% } %>
Situation 2
If you want a DisplayName of the list (??) you should create a ViewModel containing the IENumerable of Persons and a public string ListName. Now you can do something like:
<table>
<th>Model.ListName</th>
<% foreach (var item in Model) { %>
<tr>
<td>item.Property1</td>
<td>item.Property2</td>
</tr>
<% } %>
</table>
this would create a table with the name of your List (given in the ViewModel) as header and as items in the table you have your persons.
Design problem?
However, I would love to see you write some more information in your question above. Give us some more information on what you want to do. Do you want to show records of each Person in the list one-by-one? In that case I would recommend you create a Partial View where you put your table. Then you would get something like:
<% foreach (var item in Model) { %>
<% Html.RenderPartial("TablePerson",item); %>
<% } %>
tableperson.ascx:
...
Inherits="System.Web.Mvc.ViewPage<IEnumerable<RegistryTest.Models.Person>>"
...
<table>
<th>Model.DisplayName</th>
<tr>
<td>Model.Property1</td>
<td>Model.Property2</td>
</tr>
</table>
So, we need more information I'm afraid :)
If it's a collection with every entry having the same surname then try model[0].surname or model.ToList().First().surname
You don't need ToList() if its a List<T> already. Then it would be just model.First()
You are specifying that the page model is IEnumerable, and you say you would like to print a property of an element. Since you have a list of elements, you need to specify which of the elements you would like to retrieve the property from.
I you want a specific index in the list you will need to convert the IEnumerable collection to IList (ToList()), depending on the criteria, you may also be able to find the required element using something like a LINQ Single() operation.
Otherwise you could select the property from all the element in the list using Model.Select(m => m.PropertyName) which will give you a list of just this property, and then concatenate this list to a single string.

asp.net mvc 3 Model with complex property and checkboxes

I have a typed view as Item (one class that i created) with a form inside to add Items to my database. This Item class has one property called Categories that is a List (Category has 2 properties ID and Name)
Im using an editorfor in my view:
<div>
#(Html.EditorFor(e => e.Categories, "Categories"))
</div>
I created an EditorTemplatefor called "Categories.cshtml" render all the available categories:
#{
Layout = null;
}
#model List<Category>
#{
foreach (Category category in ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories)
{
#Html.Label("test", category.Name)
<input type="checkbox" name="Categories" value="#(category.ID)" />
}
}
The checkboxes are well rendered (one for every Available category in cache), but after clicking in some, and post the form, im receiving my instance of Item but with the property Categories empty.
What i have to do to receive my List Categories completely instantiated after submit the form?
Dont loop it. Let the framework generate the code for you (then, it will know how to build it back and bind it to your controller).
Just pass the list to the editor template and mvc will do the rest. Check my blog post on something similar.
Try using an index based loop. This ensures MVC will render the item's attributes in such a way that allows the default model binder to instantiate the model on post back. Also, use the Html helper for the checkbox as well:
var categories = ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories;
for (var index = 0; index < categories.Count; index ++)
{
#Html.Label("test", categories[index].Name)
#Html.Checkbox("ID", categories[index].ID)
}

Using a DisplayTemplate (with DisplayFor) for each item in a collection

I have created a DisplayTemplate for a Comment class, and placed it inside Comment/DisplayTemplates/Comment.cshtml.
Comment.cshtml is properly typed:
#model Comment
Then, I have a partial view that takes an IEnumerable<Comment> for model. In there I loop through the collection and would like to use the DisplayTemplate for the Comment class. The view, in its integrity:
#model IEnumerable<Comment>
#foreach (var comment in Model.Where(c => c.Parent == null)) {
#Html.DisplayFor(model => comment)
}
However, I get an error on the Html.DisplayFor line:
The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'System.String'.
How can I invoke the DisplayTemplate for each item in the foreach loop?
Instead of having a view that take an IEnumerable<Comment> and that all it does is loop through the collection and call the proper display template simply:
#Html.DisplayFor(x => x.Comments)
where the Comments property is an IEnumerable<Comment> which will automatically do the looping and render the Comment.cshtml display template for each item of this collection.
Or if you really need such a view (don't know why) you could simply:
#model IEnumerable<Comment>
#Html.DisplayForModel()
As far as the Where clause you are using in there you should simply remove it and delegate this task to the controller. It's the controller's responsibility to prepare the view model, not the view performing such tasks.
While the accepted answer works well most of the time, there are other cases in which we need to be aware of the element's index when rendering (i.e. add custom javascript that generates references to each element based on their index).
In that case, DisplayFor can still be used within the loop like this:
#model IEnumerable<Comment>
#for (int index = 0; index < Model.Count(); index++)
{
#Html.DisplayFor(model => model[index])
}

ASP.NET MVC - Insert or Update view with IEnumerable model

I've seen plenty of examples (NerdDinner, Sanderson's Sports Store, etc.) where a view is bound to a collection of objects. The syntax in the view is usually something like this...
<%# Page... Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyViewModel>>" %>
...
<% foreach (var myViewModel in Model) { %>
I've also seen plenty of examples of inserts or updates where the controller automatically binds the model parameter to the form elements in the view.
I'm looking for a mix of the two techniques where my view has form elements pertaining to a collection of myViewModels where each myViewModel has 3-4 properties. The intent is to allow the user to enter a set of these in one take.
Assuming this is possible, can anyone help me with the syntax? I can't figure out how to label the form elements to make the binding work.
This is possible through the built-in model binder, but you have to do a little bit of convention-based naming of your form objects. First, your action needs to take a collection:
[HttpPost]
public ActionResult CreateFoos(List<Foo> foos)
{
// I have a list of the foo objects that were posted
}
And then, in the view, let's say you wanted to make a form for each object:
<% for (int i = 0; i < Model.Count; i++) { %>
<%: Html.TextBoxFor(x => x[i].Property1) %>
<%: Html.TextBoxFor(x => x[i].Property2) %>
<%: Html.TextBoxFor(x => x[i].Property3) %>
<% } %>
Pay attention to how the controls are rendered in the HTML, because in your "create" view, you might want to have a javascript button that allows the user to add another record, and you'll have to increase the index for each additional control. It's not too hard, but I just wanted to warn you to pay attention to the source it actually generates.
The definitive answer is here: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
You need to name each field as if it were part of an array: "PropertyName[i]"

Resources