Failing getting checkboxes values on the controller - asp.net-mvc-4

I have a web page with a lot of checkboxes in the view in this form:
#using (Html.BeginForm("PerformDiagnostic", "Tests", FormMethod.Post))
{
(...)
#Html.CheckBox("Something01", false)<span>Something 01</span><br />
#Html.CheckBox("Something02", false)<span>Something 02</span><br />
(...)
<input type="submit" value="Submit" />
}
When I press submit button, I pass all the checkboxes statuses to the controller that has the following signature:
public ActionResult DoSomeTasks(FormCollection form)
{
int isSomething01Checked= Convert.ToInt32(form["Something01"]);
int isSomething02Checked= Convert.ToInt32(form["Something02"]);
....
}
In the controller I want to know for each checkbox whether it is checked or unchecked but the problem is that form["SomethingXX"] returns something like {true,false} but it is not telling me its current status (checked or unchecked). Also what return form["SomethingXX"] cannot be converted.
I have checked that if checkbox is checked, form["SomethingXX"] returns {true,false} and if it is unchecked then form["SomethingXX"] returns {false}, I do not understand why when checkbox is checked is returning {true,false} instead of {true}.
Any idea what is happening?

Maybe I'm missing something, but it seems like you're needlessly do an end-run around the MVC pattern, and therefore missing out on the convenience of pre-defined model binding. Why not just create a strongly-typed model?
public class ViewModel
{
[Display(Name="Something 01")]
public bool Something01 { get; set; }
[Display(Name="Something 02")]
public bool Something02 { get; set; }
}
Then use the HTML helper to generate check-boxes for the model properties:
#Html.CheckBoxFor(model => model.Something01)
#Html.CheckBoxFor(model => model.Something02)
And now the controller code is straight-forward. Simply call for the view-model type:
public ActionResult DoSomeTasks(ViewModel model)
{
bool isSomething01Checked = model.Something01;
bool isSomething02Checked = model.Something02;
}

Related

Razor Pages CheckBox throwing error in Edit Page

I need some help in inserting checkbox value to mssql database and retrieving the same in edit page.
This is model class
public class RequestForm{
[Key]
public int ID { get; set; }
public string OtherCompetitorsChecked { get; set; }
public string OtherCompetitorName { get; set; }
}
This is my RequestForm.cshtml file
<div class="tr">
<div class="td">
<input id="ChkOthers" style="margin-left:40px;" asp-for="RequestForm.OtherCompetitorsChecked" type="checkbox" value="Others" /> Others
</div>
<div class="td">
<input id="CompetitorsOthersName" title="Please Fill In Other Competitor Name" asp-for="RequestForm.OtherCompetitorName" type="text" class="form-control-slp" required disabled style="width:50%" />
</div>
</div>
When checking im inserting the checkbox value into database thats why i used string datatype in model class.Im able to insert the data to the database,when im fetching the data its showing error like below
InvalidOperationException: Unexpected expression result value 'Others' for asp-for. 'Others' cannot be parsed as a 'System.Boolean'.
is there any way to fix this?
InvalidOperationException: Unexpected expression result value 'Others'
for asp-for. 'Others' cannot be parsed as a 'System.Boolean'.
public string OtherCompetitorsChecked { get; set; }
This issue relates the OtherCompetitorsChecked data type. The Input Tag Helper sets the HTML type attribute based on the .NET type. Form the following list, we can see that, for the Input checkbox, the .Net type should be Bool type.
So, to solve the above issue, change the OtherCompetitorsChecked's data type to the bool type:
public class RequestForm
{
[Key]
public int ID { get; set; }
public bool OtherCompetitorsChecked { get; set; }
public string OtherCompetitorName { get; set; }
}
Then, in the Get or Post method, when you set its value, it should be true or false.
public void OnGet()
{
RequestForm = new RequestForm() {
ID = 1001,
OtherCompetitorName = "AA",
OtherCompetitorsChecked = true
};
}
Besides, in your application, might be you want to display a list of selected items using checkbox, and want to use a string type to store the selected value. If that is the case, you could try to use the html <input type="checkbox" name ="selectedCourses"> element to display the items (without using Tag helper) or use a <select> tag, then, in the Post method, get the selected option's value via the html element's name property.
Code like this (you could change the checkbox value to the model data, model detail information, refer this tutorial):
<input type="checkbox" name="selectedCompetitorName" value="Others" />
the Post method:
public void OnPost(string selectedCompetitorName)
{
if (ModelState.IsValid)
{
var data = RequestForm;
}
}
<input id="CompetitorsOthersName" title="Please Fill In Other Competitor Name" asp-for="RequestForm.OtherCompetitorName" type="text"
class="form-control-slp" required disabled style="width:50%" />
At the end, according to the above code, I assume you want to make the CompetitorsOthersName text box readonly, if that is the case, try to remove the disabled attribute, and add the readonly attribute. Because, if using the disabled attribute, after submitting the form, the submitted model's CompetitorsOthersName property will be null. You can check it.

Unobtrusive validation doesn't work with ViewComponents

I'm implementing a form with ASP.NET Core v3.1.
I have code for a drop-down on a Razor page which looks like this:
<div class="form-group">
<label asp-for="MySelectedItem" class="m-b-none"></label>
<help-text asp-for="MySelectedItem"></help-text>
<div class="input-group">
<select asp-for="MySelectedItem" asp-items="#Model.MyItems" class="form-control"></select>
<partial name="_ValidationIcon" />
</div>
<span asp-validation-for="MySelectedItem" class="validation-message"></span>
</div>
In my model class, I've included a validation rule to ensure that a valid option must be selected. This renders nicely:
Now, I want to genericise this as a View Component so that I don't have to paste this lump of code anytime I want a drop-down. Here is my implementation:
public class DropdownViewComponent : ViewComponent
{
public IEnumerable<SelectListItem> Items { get; set; }
public ModelExpression SelectedItem { get; set; }
public async Task<IViewComponentResult> InvokeAsync(ModelExpression selectedItem, IEnumerable<SelectListItem> items)
{
Items = items;
SelectedItem = selectedItem;
return View(this);
}
}
/Dropdown/Default.cshtml
#model Web.ViewComponents.DropdownViewComponent
<div class="form-group">
<label asp-for="SelectedItem" class="m-b-none"></label>
<help-text asp-for="SelectedItem"></help-text>
<div class="input-group">
<select asp-for="SelectedItem" asp-items="#Model.Items" class="form-control"></select>
<partial name="_ValidationIcon" />
</div>
<span asp-validation-for="SelectedItem" class="validation-message"></span>
</div>
Usage:
<vc:dropdown selected-item="MySelectedItem" items="Model.MyItems"></vc:dropdown>
This code correctly renders the drop-down, but the validation attributes are missing from the rendered HTML. Why?
I'm also not sure how to get the DisplayName from the model expression that I passed to the View Component.
Where have I gone wrong?
Thanks.
Presumably, your validation attributes are on the MyItems property of your parent model, which isn't listed here. E.g.,
public class ParentModel {
[Required]
public IEnumerable<SelectListItem> MyItems { get; set; }
}
As such, the issue is that your view is no longer binding to that model or its MyItems property. Instead, it's binding to the Items property on the DropdownViewComponent model, which doesn't have any validation attributes on it. Those two properties may both be pointing to the same IEnumerable<SelectListItem> object reference, but their metadata is entirely different. As such, ASP.NET Core is correctly displaying the validation attributes associated with the DropdownViewComponent.Items property.
I see this as an unfortunate limitation of view components—but one that's conceptually difficult to reason out of. For more information, see my answer to a similar question I previously posed, How to bind a ModelExpression to a ViewComponent in ASP.NET Core.
That said, given your particular requirements, you can work around this by passing the model which contains the target property to your view component—instead of passing the value of the target property itself—and then relaying that via your view component's view model:
public class DropdownViewComponent : ViewComponent
{
public ParentModel ParentModel { get; set; }
public ModelExpression SelectedItem { get; set; }
public async Task<IViewComponentResult> InvokeAsync(ModelExpression selectedItem, ParentModel model)
{
ParentModel = model;
SelectedItem = selectedItem;
return View(this);
}
}
Note: I would typically create a separate, lightweight view model for your view component, instead of passing your view component object down to its view. But I'm maintaining this structure for consistency with your original code.
You would then be able to bind to the original property in your view component's view, thus maintaining all of the original validation attributes:
<select asp-for="SelectedItem" asp-items="#Model.ParentModel.MyItems"></select>
On first approximation, this doesn't buy you much—and may even defeat your entire reason for pursuing a view component in the first place—as it forces you to operate against a single model. If multiple views with different models want to use this view component, that introduces some problems. You can mitigate these, however, by introducing a layer of abstraction.
There are a few approaches to this, such as establishing an interface, but the one I recommend is to develop a specialized list class which you use to model IEnumerable<SelectListItem>:
public class DropdownList: List<SelectListItem> {
public virtual IEnumerable<SelectListItem> Items { get; } = this;
}
And then working off of that in your DropdownViewComponent:
public class DropdownViewComponent : ViewComponent
{
public DropdownList DropdownList { get; set; }
public ModelExpression SelectedItem { get; set; }
public async Task<IViewComponentResult> InvokeAsync(ModelExpression selectedItem, DropdownList dropdownList)
{
DropdownList = dropdownList;
SelectedItem = selectedItem;
return View(this);
}
}
And, finally, implementing it as follows in your view component's view:
<select asp-for="SelectedItem" asp-items="#Model.DropdownList.Items"></select>
This would allow you to override this class in order to add attributes as needed. E.g.,
public class RequiredDropdownList : DropdownList {
[Required]
public override Items { get; } = this;
}
Note: if you have a need to use a variety of different collections on different view models, and were relying on IEnumerable<SelectListItem> to unify them, this approach won't work. In that case, creating something like an IDropdownList interface makes more sense. Regardless, the concept is virtually identical.

Asp.NET Core Model Binding input type checkbox not remains unchecked

I am facing some strange issue, I am using Asp.net Core.
I have Model class which I am binding to my razor view and here is the model implementation.
{
PatientDetailReport = new ReportModel();
itemid = true;
}
public ReportModel PatientDetailReport { get; set; }
public bool itemid { get; set; }
Report Model Class has few bool properties like
public bool IdentityNumberDisplay { get; set; }
I am trying to bind model in both of these ways as mentioned by mostly blogs and also on stackoverflow
(1) <input type="checkbox" asp-for="#Model.PatientDetailReport.IdentityNumberDisplay" />
(2) #Html.CheckBoxFor(m=>m.PatientDetailReport.IdentityNumberDisplay,new { })
</td>
but both of these cases remains unchecked.
for first case, I also tried with value=#Model.PatientDetailReport.IdentityNumberDisplay
but at jquery level I have to check it with value =True or False (as string) I am able to modify the checkbox, but the value is not posting on Controller.
Please can anyone guide me regarding to this case.
Why the case 2 is not working,however most of the blogs are saying to use like that?
Thanks
I could reproduce your problem, make sure PatientDetailReport.IdentityNumberDisplay has been set to true in your case.
Since your code does not assign value to it and the default value false results in checkbox unchecked.
You could set default value like below to have a test:
public bool IdentityNumberDisplay { get; set; } = true;
<form method="post">
#Model.PatientDetailReport.IdentityNumberDisplay
#*see the value of IdentityNumberDisplay*#
<input type="checkbox" asp-for="#Model.PatientDetailReport.IdentityNumberDisplay" />
#Html.CheckBoxFor(m=>m.PatientDetailReport.IdentityNumberDisplay,new { })
<input type="submit" value="submit" />
</form>
I resolved this issue using JQuery by checking Checkbox value from Model which is coming as 'True' in string format, and then assigned checked property to true.
#Html.CheckBoxFor(m=>m.PatientDetailReport.IdentityNumberDisplay
,new {value=Model.PatientDetailReport.IdentityNumberDisplay })
$.each($("input[type='checkbox']"), function (e) {
console.log($(this).val());
//debugger;
if ($(this).val() === 'True') {
$(this).prop('checked', true);
}
})
I am not sure it is correct or not but this resolved my issue for asp.net core model's bool property for checkbox or radio button.

How to Use Two Same Model in a View? [MVC4]

I'm trying to create a status update page where I want a user to insert status message in Index page and also, I want to show all inserted all status messages in the same Index page.
This is my Model code:
public class Statuses
{
[Key]
public int StatusID { get; set; }
[DataType(DataType.MultilineText)]
[Required]
public string message { get; set; }
}
public class StatusContext : DbContext
{
public DbSet<Statuses> Status { get; set; }
}
And, I used #Html.EditorFor(model => model.message) in the Index.cshtml page.
To show the editor, I used the following model in View.
#model LearnStart.Models.Statuses
However, to show all the status messages below the Multiline TextArea, I think I'm supposed to use the below one.
#model IEnumerable<LearnStart.Models.Statuses>
How to use both model in same view so that I can display both the text area (to insert the status message) and to list all available status messages below it?
First, you should not be passing your entities directly to your view. The recommended best practice is to use View Models, which are models tailored specifically to your view.
Second, when using a view model you can now do this, since it's not tied to your data model entities:
public class MyActionViewModel {
public List<StatusesViewModel> StatusList {get;set;}
public StatusesViewModel CreatedStatus {get;set}
}
Then in your view:
#model MyActionViewModel
#Html.EditorFor(x => x.CreatedStatus)
.............................................
#Html.DisplayFor(x => x.StatusList)
Then you can create two templates, an EditorTemplate and a DisplayTempate:
In ~/Views/Shared/EditorTemplates/StatusesViewModel.cshtml
#model StatusesViewModel
#using(Html.BeginForm()) {
#Html.EditorFor(x => x.Message)
<input type="submit" value="Create Status" />
}
In ~/Views/Shared/DisplayTemplates/StatusesViewModel.cshtml
#model StatusesViewModel
<div>
<span>#Model.Message</span>
</div>
The thing that's nice about using the templates is that they will automatically iterate over your collection.. no foreach or for statement is used. A single EditorFor works on the entire collection, then renders the template based on the type, which in this case translates to StatusViewModel.cshtml
Easy way is to put a list inside Viewbag and show list in View as shown :-
Controller :
Public Actionresult Myaction()
{
.........
Viewbag.data = //bind list of messages here
return View();
}
View :
#model LearnStart.Models.Statuses
.........
.........
.........
#if(Viewbag.data != null){
<table>
#foreach(var item in Viewbag.data)
{
<tr><td>#item.message</td></tr>
}
</table>
}

ASP.NET MVC 4 - ListBoxFor, send selectedValue in ActionLink

I have a list of model. I want to retrieve the listBoxSelectedValue to send it in my actionLink to edit it.
This is my view :
#using (Html.BeginForm())
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis,"ID","Name", Model.SelectedApplis))<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})<br/>
#Html.ActionLink("Delete","Delete","Application")<br/>
}
I created a class "ListBoxApplication" with the List which will contain the selectedValue of the ListBox.
public class ListBoxApplication
{
public IEnumerable<int> SelectedApplis { get; set; }
public ListBoxApplication()
{
SelectedApplis = new List<int>();
}
}
I have 2 controllers : Application and Home
In HomeController, I created the model ListBoxApplication which contain the List. In my ViewBag.Applis, i have all my ApplicationModel.
public ActionResult Index()
{
ListBoxApplication listeApplis = new ListBoxApplication();
ViewBag.Applis = ApplicationModels.GetListApplications();
return View(listeApplis);
}
In my ApplicationController :
public ActionResult Edit(ListBoxApplication listAppId)
{
// I WANT TO RETRIEVE MY listAppId HERE, but it is always 'null'
return View();
}
So I think my problem is in the actionLink :
#Html.ActionLink("Edit","Edit","Application", null, new { listAppId = Model.SelectedApplis})
Me Edit Method is not is the actual controller (Home/Index). I need to send the selectedValue of my ListBox in my actionLink to (Application/Edit).
The listAppId is always 'null'. It doesn't retrieve the value... Is there a mistake in my actionLink ?
Thanks for advance
I don't believe that action links will trigger a postback to the server. Try this instead:
#Html.ActionLink("Delete","Delete","Application")<br/>
#Html.ActionLink("Add","Create","Application")<br/>
#using (Html.BeginForm("Detail","Application"))
{
#Html.ListBoxFor(a => a.SelectedApplis, new SelectList(ViewBag.Applis)) //not sure what the other params you had here were for, but it should work like this
<br/>
<input type="submit" name="Edit" value = "Edit"/>
#*added in response to comment*#
<input type="submit" name="Delete" value = "Delete"/>
<input type="submit" name="Add" value = "Add"/>
}
If you plan on having all of those buttons post back to the server, you could also use ajax (and javascript) to accomplish this same goal, without needing to write out a form for each individual button. Both ways would work just fine, multiple forms is technically easier though.
public ActionResult Detail(ListBoxApplication listAppId, bool Edit, bool Add, bool Delete)
{
if(//check your bools here){
}
return View();
}