ASP.NET CORE Checkbox how to check for true? - asp.net-mvc

How do I check if a checkbox value is true when the result is either {false} for not checked and {true,false} for checked?
When I add value="yes" to the helper i get {yes,false} for checked. {Sidebar: What is so wrong with Microsoft they can't get this right?}
{Versions: netcoreapp3.1, Microsoft.EntityFrameworkCore 3.1.5, Microsoft.VisualStudio.Web.CodeGeneration.Design 3.1.3, VS Enterprise 2019 16.6.3}
View:
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="MyBoolValue" /> #Html.DisplayNameFor(model => model.MyBoolValue)
</label>
</div>
Controller: (with IFormCollection form)
if(form["MyBoolValue"].ToString() != "") // Does not work when the value is {false} not checked
{
DbTable.MyBoolValue = true;
}
else
{
DbTable.MyBoolValue = false;
}
I've tried many combinations like:
if(Convert.ToBoolean(form["MyBoolValue"]) == true)
Anyone have a simple way to get a true false condition consistently from a checkbox?

The second field is a hidden field. It will be submitted regardless whether the checkbox is checked or not. If the checkbox is checked, the posted value will be true,false. Else,the posted value will be false.It is caused by design.
If you want to check the checkbox is true or false,You can do like this:
Controller:
public IActionResult Test(IFormCollection form) {
string[] str = form["MyBoolValue"].ToArray();
//the first solution
if (str.Length > 1)
{
//MyBoolValue is true
}
else {
//MyBoolValue is false
}
//the second solution
if (str[0].Equals("true"))
{
//MyBoolValue is true
}
else
{
//MyBoolValue is false
}
return View();
}
}

Related

MVC Not Parsing the Correct Value from Model

Can anyone tell me why, if my model clearly shows my values to be "true" or "false" MVC still thinks it says, "true,false." I assume this is because it is confusing the Request with my Model; however, I am explicitly telling it to use the Model.
As you can see in the image above, the model value is "true." However, in the image below, it thinks the value is "true,false." How can I make it just use "true?"
Model
public class TagCategorySearchViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<TagSearchViewModel> Tags { get; set; }
}
public class TagSearchViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
Controller
[Authorize]
public ActionResult FilterPrereqGrid(EditStudentPrerequisitesViewModel model, int page = 1)
{
model.Prerequisites = new List<PrerequisiteListViewModel>();
var businessPartner = _bpManager.GetBusinessPartnerByMapGuid(model.BusinessPartnerMapGuid);
model.Prerequisites.AddRange(_epManager.GetFullPrerequisitesLeftJoinedWithExperience(model.ExperienceId, businessPartner?.Id));
// fix for how MVC binds checkboxes... it send "true,false" instead of just true, so we need to just get the true
for (int i = 0; i < model.TagCategories?.Count(); i++)
{
for (int j = 0; j < model.TagCategories[i].Tags?.Count(); j++)
{
model.TagCategories[i].Tags[j].IsSelected = bool.Parse((Request.QueryString[$"TagCategories[{i}].Tags[{j}].IsSelected"] ?? "false").Split(',')[0]);
}
}
var selectedTagIds = model.TagCategories?.SelectMany(x => x.Tags).Where(x => x.IsSelected == true).Select(x => x.Id).ToArray();
// filter by selected tags
if (selectedTagIds.Any())
{
model.Prerequisites = (from p in model.Prerequisites
let prereqTagIds = p.Prerequisite.PrerequisiteTags.Select(et => et.TagId)
where selectedTagIds.All(x => prereqTagIds.Contains(x))
select p).ToList();
}
model.Prerequisites = (from m in model.Prerequisites
let ownerDocs = _deManager.GetDocumentsByOwnerAndSourceId(model.BusinessPartnerMapGuid, m.Prerequisite.Id).OrderByDescending(e => e.CreatedDate)
select new PrerequisiteListViewModel
{
Prerequisite = m.Prerequisite,
Selected = m.Selected,
Mandatory = m.Mandatory,
HasExpiration = m.Prerequisite.HasExpiration,
BusinessExpirationPeriod = m.Prerequisite.ExpirationPeriod == 0 ? "None" : m.Prerequisite.ExpirationPeriod.ToString(),
DocOwnerGuid = (ownerDocs.Any() ? model.BusinessPartnerMapGuid : Guid.Empty),
DocRowGuid = (ownerDocs.Any() ? ownerDocs.First().DocRowguid : Guid.Empty),
HasDocuments = ownerDocs.Any()
}).ToList();
int rowsPerPage = 1;
model.TotalRecords = model.Prerequisites.Count();
model.Prerequisites = model.Prerequisites.Skip(page * rowsPerPage - rowsPerPage).Take(rowsPerPage).ToList();
return PartialView("~/Views/BusinessExperience/_EditPrerequisites.cshtml", model);
}
That is by design. The Html.CheckBoxFor extension actually renders something like the following:
<input type="checkbox" .. />
<input type="hidden" />
So both of these have the same name as the property you are rendering out. When checked, the checkbox returns "True", but when unchecked, the checkbox returns nothing. That is the way checkboxes work with form posts. In order to determine that nothing was checked, the MVC framework included a hidden field so that it would return "False" when not checked, but "True,False" when checked (since multiple items with the same name return this way from the form post). And then MVC model binding converts "True,False" to true.
You could just render your own <input type="checkbox" /> with a value of true, and that would just return true, but when unchecked, nothing gets rendered. Be aware of that...
This actually works, it's a shame I can't just do the obvious way and have to write this all out though!
#model List<Prep2PracticeWeb.Models.ViewModels.TagCategorySearchViewModel>
#if (Model != null)
{
<div class="tag-categories">
#for (int i = 0; i < Model.Count(); i++)
{
#Html.HiddenFor(x => Model[i].Id)
#Html.HiddenFor(x => Model[i].Name)
<h4 data-toggle="collapse" data-target="#CollapsableTagCategory_#Model[i].Id" aria-expanded="false" aria-controls="CollapsableTagCategory_#Model[i].Id">
<span class="glyphicon glyphicon-chevron-right"></span>
<span class="glyphicon glyphicon-chevron-down"></span>
#Model[i].Name
</h4>
<div id="CollapsableTagCategory_#Model[i].Id" class="tag-container collapse">
#if (Model[i].Tags != null)
{
for (int j = 0; j < Model[i].Tags.Count(); j++)
{
#Html.HiddenFor(x => Model[i].Tags[j].Id)
#Html.HiddenFor(x => Model[i].Tags[j].Name)
#* the following commented out line won't work because MVC is funny *#
#*<label>#Html.CheckBoxFor(x => Model[i].Tags[j].IsSelected) #Model[i].Tags[j].Name</label>*#
<label>
<input #(Model[i].Tags[j].IsSelected ? #"checked=""checked""" : string.Empty) data-val="true" data-val-required="The IsSelected field is required." id="TagCategories_#(i)__Tags_#(j)__IsSelected" name="TagCategories[#i].Tags[#j].IsSelected" type="checkbox" value="true"> #Model[i].Tags[j].Name
<input name="TagCategories[#i].Tags[#j].IsSelected" type="hidden" value="false">
</label>
}
}
</div>
}
</div>
}
Update: I realized GET was not an appropriate strategy in this scenario given the fact that the querystring can only be so long before the server returns a 500 error. I switched to POST which alleviated the issue.

MVC default to checked based on value in item

I have an MVC 5 project and we are creating radio buttons from database records.
It works fine below but there has to be a way to NOT have the if check in there and either append the checked after the fact or even before.
As shown below, the only difference between the 2 radio button creations is: new { #checked = "checked" }
#{
foreach (var item in Model.PaintColor)
{
if (item.DefaultChoice == true)
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString(),
new { #checked = "checked" }
)
}
else
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString()
)
}
#Html.Label("Color" + item.ColorId, item.ColorDesc)
}
<br />
}
See comments from Stephen regarding fixing this. Basically setting it as checked as not required as since it checks the value that it is being bound to.
When the view model is initially created I set the field value to the Default value and the radio button is set as checked.
foreach (var item in Model.PaintColor)
{
var idVal = string.Format("PaintColor_{0}", item.ColorId);
<div>
<label>
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId,
new { id = string.Format("Rb{0}", idVal) }
)
<span>#item.Description</span>
</label>

MVC CheckBox Rendering on Views

I am having difficulties trying to render a checkBox on my Partial View. Basically, what I want is to render the checkBox based on a value extracted from the database. Please see my code below:
<div class="editor-label">
#Html.LabelFor(model => model.Active)
</div>
<div class="editor-field">
#{if (Model.Active == 'Y')
{ Html.CheckBox("Active", true); }
else
{ Html.CheckBox("Active", true); }
}
</div>
In this code block, I am checking for the value inside the Active field from my model and renders the isChecked property of the checkBox to true or false based on that value.
I have debugged this code and used a breakpoint. I have a value of 'Y' from my database and it did went through the if statement. However, when the form pops up, the checkBox didn't render.
Can someone please help me? Thanks!
I think your main problem may be because you have #{ instead of just # before the if.. try remove that.
also, to make things easier.. create a new property on your view model:
public bool IsActive
{
get { return Active == "Y"; }
}
then on your view, use Html.CheckBoxFor(m=> m.IsActive)

unobtrusive MVC3 validating group of checkboxes

I need to validate a group of checkboxes using MVC3 unobtrusive validation. how would i do it? I found this and tried it, but it doesn't work.
$(function(){
$.validator.addMethod('cb_selectone', function(value,element){
if(element.length>0){
for(var i=0;i<element.length;i++){
if($(element[i]).val('checked')) return true;
}
return false;
}
return false;
}, 'Please select at least one option');
$('#main').validate({rules:{Services:"cb_selectone"}});
...
My Html:
<input type="checkbox" class="checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox" name="Services" value="3" />
It would be best if someone provided a complete solution with server side + client side validation (of course using MVC3 unobtrusive validation).
Thanks
Ok, figured it out:
for server validation:
using data annotations (required will do it)
like so in my view model:
[Required(ErrorMessageResourceName = "fld_Service_val_Required_lbl", ErrorMessageResourceType = typeof(Resources.Service.Controllers.Firm))]
public ICollection<int> Services { get; set; }
for client validation in my html i added a class to my input checkboxes:
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="1" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="2" />
<input type="checkbox" class="checkbox required-checkbox" name="Services" value="3" />
and also:
$(function(){
$.validator.addMethod('required_group', function(value, element) {
var $module = $(element).parents('form');
return $module.find('input.checkbox:checked').length;
}, 'Select at least one Service please');
$.validator.addClassRules('required-checkbox', { 'required_group' : true });
..
not sure if this is the best solution but it works :). if someone knows a better please post.
This works - validates appropriately on submit, and hides/displays the validation message if a checkbox is subsequently checked or unchecked with minimal overhead (only gets hit once per validation cycle).
JavaScript:
(function ($) {
//functions from unobtrusive:
function setValidationValues(options, ruleName, value) {
options.rules[ruleName] = value;
if (options.message) {
options.messages[ruleName] = options.message;
}
}
var formEls;
function getUniqueFormId(form) {
if (typeof(formEls==='undefined')) {
formEls = document.getElementsByTagName('form');
}
return 'form' + Array.prototype.indexOf.call(formEls, form);
}
//from jQuery validation
function findByName(name, form) {
// select by name and filter by form for performance over form.find("[name=...]")
return $(document.getElementsByName(name)).map(function (index, element) {
return element.form == form && element.name == name && element || null;
});
}
//-------------------------
$.validator.addMethod('requiredgroup', function (value, element, params) {
for (var i = 0; i < params.length; i++) {
if (params[i].checked) { return true; }
}
return false;
});
valGroups = [];
$.validator.unobtrusive.adapters.add('requiredgroup', function (options) {
var groupName = options.element.name,
uniqueGroup = getUniqueFormId(options.form) + groupName;
if (!valGroups[uniqueGroup]) {
valGroups[uniqueGroup] = findByName(groupName, options.form);
}
//jQuery Validation Plugin 1.9.0 only ever validates first chekcbox in a list
//could probably work around setting this for every element:
setValidationValues(options, 'requiredgroup', valGroups[uniqueGroup]);
});
} (jQuery));
of course the elements must have a data-val-requiredgroup attribute. The following MVC code (as part of a helper) will add the appropriate annotations:
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
string name = ExpressionHelper.GetExpressionText(expression);
var baseAttr = htmlHelper.GetUnobtrusiveValidationAttributes(name, metaData);
baseAttr.Add("name", name);
baseAttr.Add("type", "checkbox");
if (metaData.IsRequired)
{
baseAttr.Add("data-val-requiredgroup", baseAttr["data-val-required"]);
baseAttr.Remove("data-val-required");
Because it looks for the Required attribute, server side validation is handled by the existing framework.

MVC Html.CheckBox and form submit issue

Crazy issue with submitting of values in Html.Checkbox in ASP.NET MVC RC
Some of the values are just not come to Request.Params
At my form I have this line inside the cycle:
<%=Html.CheckBox("cb" + p.Option.Id, p.Option.IsAllowed, new { value = 6 })%>
and it renders to next:
<input checked="checked" id="cb17" name="cb17" type="checkbox" value="6" />
<input name="cb17" type="hidden" value="false" />
<input checked="checked" id="cb18" name="cb18" type="checkbox" value="6" />
<input name="cb18" type="hidden" value="false" />
<input id="cb19" name="cb19" type="checkbox" value="6" />
<input name="cb19" type="hidden" value="false" />
<input id="cb20" name="cb20" type="checkbox" value="6" />
<input name="cb20" type="hidden" value="false" />
<input checked="checked" id="cb21" name="cb21" type="checkbox" value="6" />
<input name="cb21" type="hidden" value="false" />
After submitting the Form I'm get something like:
Form.Params["cb17"] = {6, "false"}
Form.Params["cb18"] = {6, "false"}
Form.Params["cb19"] = {"false"}
Form.Params["cb20"] = {"6,false"}
Form.Params["cb21"] = {"false"}
In the request string Some of the params are displayed twice (normal situation) and some only ONE TIME (only value of hidden field).
It seems that it doesn't rely on whether checkbox was checked or not, whether value has changed or so...
Does anybody faced with such a situation? How can I work around?
<% using(Html.BeginForm("Retrieve", "Home")) %>//Retrieve is the name of the action while Home is the name of the controller
<% { %>
<%foreach (var app in newApps) { %>
<tr>
<td><%=Html.CheckBox(""+app.ApplicationId )%></td>
</tr>
<%} %>
<input type"submit"/>
<% } %>
and in your controller
List<app>=newApps; //Database bind
for(int i=0; i<app.Count;i++)
{
var checkbox=Request.Form[""+app[i].ApplicationId];
if(checkbox!="false")// if not false then true,false is returned
}
the reason you check for false because the Html Checkbox helper does some kind of freaky thing for value true
True returns as:
it makes the string read "true, false"
so you may have thought it was two values but its just one and means true
False returns as:
it makes the string read "false"
This is actually the way it should work according to specifications.
It has nothing to do with ASP.NET MVC, but when a check box is left unchecked it is not included in the POST collection.
You get two values because you have both a checkbox and an input with the same name (and the ones you have two values for are most likely the ones with checkboxes checked).
Edit: specifications from W3C
Without the need to ask database about the ids after form submitting/before saving (Stateless mode) I've produced such code:
foreach (string key in Request.Form)
{
var checkbox = String.Empty;
if (key.StartsWith("cb"))
{
checkbox = Request.Form["" + key];
if (checkbox != "false")
{
int id = Convert.ToInt32(key.Remove(0, 2));
}
}
}
Thanks you guys to help me work around this issue!
I use this:
public struct EditedCheckboxValue
{
public bool Current { get; private set; }
public bool Previous { get; private set; }
public bool Changed { get; private set; }
public EditedCheckboxValue(System.Web.Mvc.FormCollection collection, string checkboxID) : this()
{
string[] values = collection[checkboxID].Split(new char[] { ',' });
if (values.Length == 2)
{ // checkbox value changed, Format: current,old
Current = bool.Parse(values[0]);
Previous = bool.Parse(values[1]);
Changed = (Current != Previous);
}
else if (values.Length == 1)
{
Current = bool.Parse(values[0]);
Previous = Current;
Changed = false;
}
else
throw new FormatException("invalid format for edited checkbox value in FormCollection");
}
}
and then call it like this:
EditedCheckboxValue issomething = new EditedCheckboxValue(collection, "FieldName");
instance.IsSomething = issomething.Current;

Resources