I use this:
<input type="checkbox" value="#item.Id" checked="#(item.HasAccess ? "checked" : "")"/>
This worked correctly: I mean when HasAccess is true then checked="checked" and when
HasAccess is false then checked="" but always the checkboxs checked, how can I use ternary operator and handle checked attribute correctly?
Unfortunately, in razor V1, you must do it this way:
<input type="checkbox" value="#item.Id" #(item.HasAccess ? "checked=\"checked\"" : "") />
This is because in the HTML world, the mere presence of the attribute at all, regardless of the value, tells the browser to check the box.
In Razor V2, this will be less of a problem. See the conditional attributes section of the article below:
http://vibrantcode.com/blog/2012/4/10/whats-new-in-razor-v2.html/
Related
Given the following viewmodel:
public class FooViewModel
{
public bool IsBoolValue { get; set; }
}
and this view:
<input type="hidden" id="Whatever" data-something="#Model.IsBoolValue" value="#Model.IsBoolValue" />
The output of the hidden input field is this:
<input type="hidden" id="Whatever" data-something="True" value="value">
How come the value attribute is not set toTrue, but the data-something attribute is?
Is there a change in MVC 5 that would cause this, since in my MVC 4 apps this problem does not occur.
I think I've figured it out.
I believe the Razor viewengine is adhering to the HTML 5 way of setting boolean attributes, as described here:
What does it mean in HTML 5 when an attribute is a boolean attribute?
In HTML 5, a bool attribute is set like this:
<input readonly />
or
<input readonly="readonly" />
So the Razor viewengine takes your model's bool value and will render (in my case) the value attribute if Model.IsBoolValue is true. Otherwise, if it's false then the value attribute is not rendered at all.
EDIT:
As mentioned Zabavsky in the comments, to force the value of True or False to appear in the value attrbiute, simple use ToString():
<input type="hidden" value="#Model.BoolProperty.ToString()" />
<div class="field_warp_hidden">
<input type="checkbox" asp-for="ShowGoogleCaptcha" checked="#Model.ShowGoogleCaptcha" value="#Model.ShowGoogleCaptcha"/>
</div>
can set field_wrap_hidden is display:none;
checked and value must be set
I was using the hidden field in a partial view and I got an error when I used .ToString()
alternative option was to specify the value property explicitly even after specifying asp-for
<input type="hidden" value="#Model.TargetmarketsToChangeAvailability[i].Available" asp-for="TargetmarketsToChangeAvailability[i].Available" />
I have a model containing some complex types including nested lists. So I cannot simply use the model.
My initial thought is using knockoutjs to take care of the user interactions (add/delete list items etc.), and post knockoutjs view model via ajax.
But after some googling, I found I could give meaningful name attributes in my form elements and do regular form submit to post my form model like this.
<input checked="checked" data-val="true" data-val-required="The Boolean field is required." name="Questions[0].SubQuestions[0].OptionAnswers[0]" type="checkbox" value="true">
Of course I still need some javascript for list manipulations and stuff.
Which approach is more preferred?
EDIT:
Sorry for the indexing issue. I was aware it should be sequential and start from 0. I just took a random element from my generated html. The indexing is not really my issue. I just would like to know which approach is more preferred.
I start liking the regular form submission way as it takes advantage of the validation feature of DataAnnotations and Html.ValidationSummary. What's the advantages for ajax post?
You are very close to understand the concept how asp.net mvc model binder works out of the box.
This is the link to an article from where I learnt how to achieve similar requirement.
The way that mvc model binder reads in the properties is by looking for parameterName[index].PropertyName. The index must be zero-based and unbroken. Say suppose, if you have your html like below which is a list for example and it can be your Questions list which is a complex type:
<input type="text" name="people[0].FirstName" value="George" />
<input type="text" name="people[0].LastName" value="Washington" />
<input type="text" name="people[1].FirstName" value="Abraham" />
<input type="text" name="people[1].LastName" value="Lincoln" />
<input type="text" name="people[3].FirstName" value="Thomas" />
<input type="text" name="people[3].LastName" value="Jefferson" />
When you submit the above values to asp.net mvc controller, the above value turns into:
people%5B0%5D.FirstName=George&people%5B0%5D.LastName=Washington&people%5B1%5D.FirstName=Abraham&people%5B1%5D.LastName=Lincoln&people%5B3%5D.FirstName=Thomas&people%5B3%5D.LastName=Jefferson
The mvc model binder then read the above data as:
people[0].FirstName = "George"
people[0].LastName = "Washington"
people[1].FirstName = "Abraham"
people[1].LastName = "Lincoln"
people[3].FirstName = "Thomas"
people[3].LastName = "Jefferson"
So, as you can see we have list items for index 0, 1 and then 3. It is broken! This results in mvc model binder only translating this data to a list of type people for only two items i.e. index 0 and 1. It excludes 3 as there was no data at index 2.
this answer has information from the same article. If you understand this, you will understand the role of name attributes and you will appreciate how easy it is to follow the rules and mvc binder will understand all your posted data.
If you want to make sure that mvc binder constructs a list from the posted data even if any index is missing then refer Eric's answer
Yoda's answer is very very good. However there is a known way to have a list of Non-Sequential Indices for postback as Phil Haacked describes in Model Binding To A List (Non-Sequential Indices heading)
<input type="hidden" name="products.Index" value="0" />
<input type="text" name="products[0].Name" value="Beer" />
<input type="text" name="products[0].Price" value="7.32" />
<input type="hidden" name="products.Index" value="1" />
<input type="text" name="products[1].Name" value="Chips" />
<input type="text" name="products[1].Price" value="2.23" />
<input type="hidden" name="products.Index" value="3" />
<input type="text" name="products[3].Name" value="Salsa" />
<input type="text" name="products[3].Price" value="1.23" />
I'm trying to post a model back to my controller. I'm not sure why TextBoxFor works and TextBox does not.
#ModelType Models.myModel
#Code
ViewData("Title") = "Foo"
End Code
#Using (Html.BeginForm())
#Html.AntiForgeryToken()
#Html.TextBoxFor(Function(m) m.Value) <--- works
#Html.TextBox(Model.Value) <--- does not work
#Html.TextBox("Value", Model.Value) <--- works! (per answer below)
#<input type="submit" value="Save" />
End Using
Html.TextBoxFor creates right input type="text" with correct name value. You mustn't use it, but it helps in this case. Same result may be achieved even with html code:
<input type="text" name="Value" value="#Model.Value" />
Main point for asp.net mvc model binders is correct names for html controls. Html.TextBox() also does it, using first parameter as html element name, so correct usage of it should be Html.TextBox("Value", Model.Value)
I am modifying an existing asp.net mvc application that creates a list of checkboxes from a list on the model, with property name "MyModelProperty" and additionally generates one more input element for "Select All" which has the following html:
<input name="MyModelProperty_SelectAll" type="checkbox" CHECKED="checked" value=""/>
What is the property declaration in the model that would create a boolean property that would bind to this existing element in the view?
I tried, 'public bool MyModelProperty_SelectAll {get;set;}' but it was returning null. Is that because the value is an empty string in the html input control?
Change your Model property for string:
public string MyModelProperty_SelectAll { get; set; }
Set some value for the checkbox, then in the server, if its checked, the value will be the given value, else you will see null.
<input name="MyModelProperty_SelectAll" type="checkbox" value="all"/>
EDIT:
If you want to bind this to a bool, you must provide a value="true" and a hidden field:
<input class="input-validation-error" id="MyModelProperty_SelectAll" name="MyModelProperty_SelectAll" type="checkbox" value="true">
<input name="MyModelProperty_SelectAll" type="hidden" value="false">
This example code above was generated using the Html.CheckBox helper.
If you don't use MVC htmlhelpers for generating checkboxes you must add an additional hidden element for your checkbox:
<input name="MyModelProperty_SelectAll" type="checkbox" value="true"/>
<input name="MyModelProperty_SelectAll" type="hidden" value="true"/>
I have some problems with ASP.NET MVC’s default model binder. The View contains HTML like this:
<input name="SubDTO[0].Id" value="1" type="checkbox">
<input name="SubDTO[1].Id" value="2" type="checkbox">
This is my simplified ‘model’:
public class SubDTO
{
public virtual string Id { get; set; }
}
public class DTO
{
public List<SubDTO> SubDTOs { get; set; }
public DTO()
{
SubDTOs = new List< SubDTO>();
}
}
All this works fine if the user selects at least the first checkbox (SubDTO[0].Id). The controller ‘receives’ a nicely initialised/bound DTO. However, if the first check box is not selected but only, for example, SubDTO[1].Id the object SubDTOs is null. Can someone please explain this ‘strange’ behaviour and how to overcome it? Thanks.
Best wishes,
Christian
PS:
The controller looks like this:
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public RedirectToRouteResult Create(DTO DTO)
{
...
}
PPS:
My problem is that if I select checkbox SubDTO[0].Id, SubDTO[1].Id, SubDTO[2].Id SubDTOs is initialised. But if I just select checkbox SubDTO[1].Id, SubDTO[2].Id (NOT the first one!!!) SubDTOs remains null. I inspected the posted values (using firebug) and they are posted!!! This must be a bug in the default model binder or might be missing something.
This behavior is "by design" in html. If a check-box is checked its value is sent to the server, if it is not checked nothing is sent. That's why you get null in your action and you'll not find value in the posted form either. The way to workaround this is to add a hidden field with the same name and some value AFTER the check-box like this:
<input name="SubDTO[0].Id" value="true" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="true" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
In this way if you check the check-box both values will be sent but the model binder will take only the first. If the check-box is not checked only the hidden field value will be sent and you\ll get it in the action instead of null.
I think this post on Scott Hanselman's blog will explain why. The relevant line is:
The index must be zero-based and unbroken. In the above example, because there was no people[2], we stop after Abraham Lincoln and don’t continue to Thomas Jefferson.
So, in your case because the first element is not returned (as explained by others as the default behaviour for checkboxes) the entire collection is not being initialized.
Change the markup as follows:
<input name="SubDTOs" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTOs" value="<%= SubDTO[1].Id %>" type="checkbox">
What's being returned by your original markup is an unrelated set of parameters, i.e. like calling RedirectToRouteResult Create(SubDTO[0].id, SubDTO[1].id, ..., SubDTO[n].id) which is clearly not what you want, you want an array returned into your DTO object so by giving all the checkboxes the same name the return value to your function will be an array of ids.
EDIT
Try this:
<input name="SubDTO[0].Id" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="<%= SubDTO[1].Id %>" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
You have to return something to make sure there is an element for each index, I suspect that any gap will cause a problem so I'd suggest using a 'null' ID, for example 0 or -1 and then process that out later in your code. Another answer would be a custom model binder.
There is always the alternate option of adding a property to your class that takes an array of strings and creates the SubDTO array from that.
public List<string> SubDTOIds
{
get { return SubDTO.Select(s=>s.Id).ToList(); }
set
{
SubDTOs = new List< SubDTO>();
foreach (string id in value)
{
SubDTOs.Add(new SubDTO { Id = id });
}
}
}
or something like that