MVC3 collection model binding with EditorFor - asp.net-mvc-3

Regarding this post and this other one.
Suppose I have the following:
public class Foo
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class BarViewModel
{
public string Baz { get; set; }
public IList<Foo> Foos { get; set; }
}
And I have a view that receive a BarViewModel:
#model BarViewModel
#Html.EditorFor(model => model.Baz)
<table>
#for(int i = 0 ; i < Model.Foos.Count ; i ++)
{
string name1 = "Foos[" + i.ToString() + "].Value1";
string name2 = "Foos[" + i.ToString() + "].Value2";
<tr>
<td>
<input type="text" name="#name1" value="#Model.Foos[i].Value1" />
</td>
<td>
<input type="text" name="#name2" value="#Model.Foos[i].Value2" />
</td>
</tr>
}
</table>
And in my controller I have a POST method that recive the BarViewModel.
Given the inputs names generated for Value1 and Value2 are "Foos[0].Value1" and "Foos[1].Value1" and so on, the collection on the BarViewModel, in the POST method, is automatically filled by the ModelBinder. Awesome.
The problem is, if I do it this way in my view :
#for(int i = 0 ; i < Model.Foos.Count ; i ++)
{
<tr>
<td>
#Html.EditorFor(model => model.Foos[i].Value1);
</td>
<td>
#Html.EditorFor(model => model.Foos[i].Value2);
</td>
</tr>
}
Then the names generated for the input are like "Foos__0__Value1", and that break the model binding. The Foos property of my BarViewModel, in my POST method, is now null
I am missing something?
Edit
If I use EditorFor on the collection itself:
#EditorFor(model => model.Foos)
The names are generated correctly. But that force me to build a ViewModel in /Views/Share to handle the type Foos, that will generate the row, wich I dont really want to do...
Edit 2
I will clarify my question here, I understand that it's a bit vague.
If I do :
#EditorFor(model => model.Foos)
The names of the inputs will have the form "Foos[0].Value1" and the model binding works just fine on posts.
But if I do :
#for(int i = 0 ; i < Model.Foos.Count ; i ++)
{
#EditorFor(model => Model.Foos[0].Value1)
}
The names takes the form "Foos__0__Value1" and the model binding does not works. On my post method, model.Foos will be null.
Is there a reason why the second syntax breaks the model binding?

I'm not entirely sure just exactly what your question is. However, this is how MVC works. EditorFor uses EditorTemplates, and you define an editor template for your type. It doesn't have to go in Share, it can go in whatever level you're using. For instance, you can have it at /Views/Home/EditorTemplates/Foos.cshtml, so long as you aren't using it anywhere other than your Home controller.

Related

viewmodel return null on HttpPost

i have created a view like below.
<%for (var i = 0; i < Model.customerCDTO.Count();i++)
{%>
<tr>
<td style="text-align: center; width: 10%;" class="table-content-middle">
<%if (Model.customerCDTO[i].Active == true)
{%>
<%=Html.CheckBoxFor(m=>m.customerCDTO[i].Active)%>
<%}
else
{ %>
<%=Html.CheckBoxFor(m => m.customerCDTO[i].Active)%>
<%} %>
</td>
<td class="table-content-middle" align="center" style="width: 80%;">
<%=Html.DisplayTextFor(m=>m.customerCDTO[i].Name)%>
</td>
</tr>
<%} %>
the problem with the above view is that it is giving value for null for customerCDTO[i].Name
below is my view model
public class CustomerVM
{
public List<CustomerCDTO> customerCDTO { get; set; }
}
In the above view model I have created a List property. The CustomerCDTO class definition is as follows.
public class CustomerCDTO
{
public string Name { get; set; }
public bool Active { get; set; }
public bool? IsChecked { get; set; }
}
please help.
thanks
It's giving you a null value because your form does not contain any <input> elements corresponding to the Name property (which you can immediately spot if you View Source). The reason for this is that DisplayTextFor is not supposed to create such elements in the first place.
An almost always bad idea would be to also call Html.HiddenFor, which does create input elements:
<td class="table-content-middle" align="center" style="width: 80%;">
<%=Html.DisplayFor(m=>m.customerCDTO[i].Name)%>
<%=Html.HiddenFor(m=>m.customerCDTO[i].Name)%>
</td>
This would solve your problem, but then the user could simply edit the values with a tool like Firebug and change the value of Name, which you obviously do not want since otherwise you 'd have presented an <input> element for that. (Note: I also changed DisplayTextFor to DisplayFor, which in general is the more correct way to do things).
But what to do then? Well, there's no magic solution here. If the form doesn't contain Name then it will be null, but if it does contain Name (even hidden) then the user can change it at will. If that's a problem, the only option is to modify your backend code to populate Name from your data store.
Could you make sure that your list is populated with your intended data after running your query against database. In fact, it seems like your data is not populated properly.

ASP.NET MVC 3 WITH RAZOR : How to pass selected checkbox' ids in a Partial view to controller action?

I have a partialview [_SearchProduct] within the main view, let's say [product] view. The Partialview has a number of checkboxes segregated into different sections like search by company,search by product etc. with one [search] button.
A User can select multiple checkboxes. When user clicks [search] button I need to pass ids of all selected checkbox to controller action and re-render the page again considering the user's selection . Please guide me how to pass selected checkbox ids to my controller action.
My partial view is something like below:
<fieldset>
<legend>By Company</legend>
<table style="border-style: none;">
<tr>
#{
int i = 0;
foreach (var item in Model.CompanyName)
{
i = i + 1;
<td style="border-style: none;text-decoration:none;" >
#Html.CheckBox("chkCompany",new {id="chkCompany_" + Model.CompanyId.Tostring()}) #Model.CompanyName
</td>
if (i == 5)
{
#:</tr><tr>
i = 0;
}
}
}
</tr>
</table>
</fieldset>
<fieldset>
<legend>By Product</legend>
<table style="border-style: none;">
<tr>
#{
i = 0;
foreach (var item in Model.Product)
{
i = i + 1;
<td style="border-style: none;text-decoration:none;" >
#Html.CheckBox("chkProduct",new {id="chkProduct_" + Model.CompanyId.Tostring()}) #Model.ProductName
</td>
if (i == 10)
{
#:</tr><tr>
i = 0;
}
}
}
</tr>
</table>
</fieldset>
checkboxes are dynamic
Checkbox id represent the primarykey of respective table based on which i do filtering.
Please guide me>>
So it sounds like you have a structure containing names (of companies/products), and ids.
I would create a View Model structure that looked like
public class PartialViewModel //Make sure this is included in your main view model
{
public List<ObjectsToInclude> Companies { get; set; }
public List<ObjectsToInclude> Products { get; set; }
}
public class ObjectsToInclude //Give this a better name
{
public string Name { get; set; }
public string Id { get; set; }
public bool Include { get; set; }
}
Then in order to bind them you could do
for (int i =0; i<Model.Companies.Count(); i++)
{
<td style="border-style: none;text-decoration:none;" >
#Html.HiddenFor(m => m.Companies[i].Id)
#Html.CheckBoxFor(m => m.Companies[i].Include) #Model.Companies[i].Name
</td>
if (i == 5)
{
#:</tr><tr>
i = 0;
}
}
Then provided your post takes a parameter of PartialViewModel (or some MainViewModel where that contains an instance of PartialViewModel), you'll have lists of companies and products binded. You can loop through the list, and take the respective ids of anything checked to be included.
Edit: If you wanted a single comma separated array to be posted, it would be possible by by creating an onclick event for your checkboxes, and then setting a value of a hidden input every time a checkbox is clicked. But then your code would only work with JavaScript enabled. If you need a comma separated string, you can create it server side with the view model I suggested.
string companyIds = String.Join(",", model.Companies
.Where(company => company.Include)
.Select(company => company.Id));
http://dotnetnsqlcorner.blogspot.in/2012/09/multiple-checkboxes-in-mvc-3-and-post.html

MVC3 Some of my fields validate while some do not

Im sitting here scratching my head with a validation problem in ASP MVC3.
Somehow I'm able to validate the field Quantity, but the field OrderNumber does not validate. I can leave it empty and it still accepts it. I've tried to add other restrictions to it as well (such as max and min length) but same result - it accepts anything.
I also try changing 'TextBoxFor' to 'EditorFor' - but it's the same result.
Quantity on the other hand works as I want it. It requires you to enter an integer and it cannot be blank.
Hopefully some of you will be able to see what I'm doing wrong here :)
Here is my model:
public class Order
{
[Required(ErrorMessage="Insert Ordernumber (6-digits)")]
public string OrderNumber { get; set; }
[Required]
public string Partnumber { get; set; }
[Required]
public long Quantity { get; set; }
public Order()
{
}
}
And here is my view :
model POWeb.Models.AddModel
#using (Html.BeginForm("Add", "Home", FormMethod.Post))
{
//Create table
<table>
<tr>
<td>Select Partnumber to produce</td>
<td>#Html.DropDownListFor(model => model.SelectedPartNumber, Model.PartNumbers)</td>
</tr>
<tr>
<td>Enter PO number</td>
<td>#Html.TextBoxFor(model => model.OrderNumber)#Html.ValidationMessageFor(model => model.OrderNumber)</td>
</tr>
<tr>
<td>Quantity</td>
<td>#Html.TextBoxFor(model => model.Quantity)#Html.ValidationMessageFor(model => model.Quantity)</td>
</tr>
<tr>
<td colspan="2">
<button type="submit" name="SubmitButton">Add</button>
</td>
</tr>
</table>
}
You have the view of type POWeb.Models.AddModel, but you try to validate Order type. I'm pretty sure validation attributes on those types are not the same, so you get problems
Anders,
My 'guess' is that your ViewModel model POWeb.Models.AddModel isn't mirroring the [Required] attribute on OrderNumber. Can you add the definition of AddModel to your question for verification on that please as it's more than likely that the Order class differs.

How to set Id for EditorFor() html helper in asp .net MVC3

I used this to override framework generated id #Html.EditorFor(model => model.cost, new { id = "checkCost" })But unfortunately id is not changing. i.e When i use string d = Request.Form["checkCost"];
at controller action then string filled with null value.WHY?? I also replaced EditorFor with TextBoxFor but nothing changed then also controller takes cost as ID
<td >
#Html.DropDownList("check_master", "--select package--")
</td>
<td >
#Html.EditorFor(model => model.check_name)
#Html.ValidationMessageFor(model => model.check_name)
</td>
<td >
#Html.EditorFor(model => model.cost, new { id ="checkCost" })
#Html.ValidationMessageFor(model => model.cost)
</td>
Updated:
This is my partial view and my main view(rendering partial from ajax.actionLink) also hold a EditorFor(model=>mode.cost)
Partial view is strongly typed to Check_master table and main view is strongly typed to package_master where both entitySet hold same name property i.e "COST" Secondly both partial and main view hold EditorFor "cost"I tried to change the name of check_master property like by
[Column("check_cost")]
public Nullable cost { get; set; }But dont know why this is not working i.e property naem is not changing while referring it like
model.something
Create 2 view models with properties CheckCost and PackageCost that way there's a seperation:
public class CheckViewModel{
public string CheckCost{get;set;}
}
public class PackageViewModel{
public string PackageCost{get;set;}
}
This way avoid conflicts.
To specifically answer the question asked, try this:
#Html.EditorFor(model => model.cost, new { #id = "checkCost" })
Note the slight (but important) difference: ... new { #id = "checkCost" })

How can I retrieve the List in my controller?

My controller always gets "null" for the "adjModel" parameter.
How can I retrieve the values?
CONTROLLER
[HttpPost]
public ActionResult AdjustmentList(List<AdjustmentVM> adjModel)
{
// adjModel is null
}
VIEW
#model List<ExtFramework.ViewModels.BillingArea.AdjustmentVM>
<div class="no-fancybox">
#using (Html.BeginForm("AdjustmentList", "Deposit", new { depositId = ViewBag.depositId }))
{
<div>
<table id="adjustment">
<tr>
<th>Description</th>
<th>Montant</th>
</tr>
#foreach(var item in Model)
{
<tr>
<td>#Html.TextBoxFor(model => item.Description)</td>
<td>#Html.TextBoxFor(model => item.Amount)</td>
</tr>
}
</table>
<input type="submit" value="" class="save" />
</div>
}
</div>
MODEL
namespace ExtFramework.ViewModels.BillingArea
{
public class AdjustmentVM
{
public int AdjustmentId { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
public int DepositId { get; set; }
}
}
This is where editor templates are useful. Instead of using a foreach loop to go through the list of view models, use #Html.EditorFor(m => m). Then, in a subfolder named EditorTemplates (an MVC naming convention) add a view with the name AdjustmentVM.cshtml. Again, this is another MVC naming convetion - using the name of the type being used. This file would look like:
#model AdjustmentVM
<tr>
<td>#Html.TextBoxFor(model => model.Description)</td>
<td>#Html.TextBoxFor(model => model.Amount)</td>
</tr>
The runtime will automatically loop over the items in the list and render the contents of the editor template, giving unique names for each form value, so that the default model binder can map these to the properties on the view model on postback.
You can customise the name of the editor template if you want, look a the UIHintAttribute class.
By default, when you want a collection, you need to make sure the names of the controls indicate they are from an array, etc. The default binder doesn't have this magic to my knowledge.

Resources