Razor Pages CheckBox throwing error in Edit Page - asp.net-core

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.

Related

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.

ASP.Net Core Razor Pages: How to return the complex model on post?

I created a new ASP.Net Core 2 (Razor Pages) Project
My model is:
public class FormularioGenerico
{
public FormularioGenerico()
{
}
public string IP { get; set; }
public List<string> items { get; set; } = new List<string>();
}
On the page I put
on the page.cshtml.cs
public class EditarModel : PageModel
{
[BindProperty]
public FormularioGenerico ff { get; set; }
[BindProperty]
public string Message { get; set; }
public void OnGet()
{
this.ff = new FormularioGenerico();
ff.IP = "C# FORM";
ff.items.Add("OK1");
ff.items.Add("OK2");
ff.items.Add("OK3");
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var m = ModelState.IsValid; // true
Debug.WriteLine(this.ff.IP); // is Always returning null
Debug.WriteLine(this.ff.items.Count); // is Always returning null
}
}
on the page.cshtml:
#model Formulario.Pages.EditarModel
...
<h1>#Model.ff.IP</h1>
#foreach (var i in Model.ff.items)
{
<div>#i</div>
}
<button type="submit">Enviar</button>
The items are correctly output. But the complete object does not go to the OnPost.
The problem is: The model is not coming fully populated on the OnPost.
How to receive the full object that was created on the OnGet, plus the changes made by the user on the form, on the post to OnPostAsync() ?
The BindProperty attribute is used to inform ASP.NET Core that the values that the form submitted should be mapped to the specified object. In your case you set the values for the ff property but you do not have the equivalent input values so that ASP.NET Core will get these values in order to store them back to the ff property.
In order to make it work you will have to replace your razor code with the following code:
<form method="post">
<h1>#Model.ff.IP</h1>
<input asp-for="#Model.ff.IP" type="hidden" /> #* create a hidden input for the IP *#
#for (int i = 0; i < Model.ff.items.Count(); i++)
{
<input asp-for="#Model.ff.items[i]" type="hidden" /> #* create a hidden input for each item in your list *#
<div>#Model.ff.items[i]</div>
}
<button type="submit">Enviar</button>
</form>
Very important. To make this work you can not use the foreach loop because ASP.NET core will not be able to find the values. You will have to use a for loop.
The inputs that I added are hidden because I guess you do not want them to be visible but you can remore the type="hidden" so that you will be able to see them. Every change that you make to these inputs will be submitted to the OnPostAsync method.

Enum DropDownList always returns the first value in the list

I've created several EditorTemplates for some Enums I'm using so that they will display as DropDownLists when I use EditorForModel(). However when I submit the form after selecting an item from the list, It always submits the first item in the dropdown instead of the selected one.
For example, if I have an enum called "Sizes" and it has the values 'Small', 'Medium', and 'Large'. EditorForModel will create a dropdown that displays them in the order 'Small', 'Medium', 'Large'. If I then select 'Large' and then look at the details for the model entry that was made into the database, it will say 'Small' which is incorrect.
Here's some example code of what I have:
Sizes.cshtml (Editor Template)
#model Options.Sizes
#{
var values = Enum.GetValues(typeof(Options.Sizes)).Cast<Options.Sizes>();
IEnumerable<SelectListItem> items =
from value in values
select new SelectListItem
{
Text = value.ToString(),
Value = value.ToString(),
Selected = (value.Equals(Options.Sizes.Small))
};
}
#Html.DropDownList("Size", items)
Options.cs (Class that holds the enum)
public class Options
{
public enum Sizes
{
Small,
Medium,
Large
};
}
Create.cshtml (View)
#model = ProjectName.Models.Ball
<form asp-action="Create" asp-controller="Ball">
#Html.EditorForModel()
<input type="submit" value="submit">
</form>
Ball.cs (Model)
public class Ball
{
[Key]
public Nullable<int> ID { get; set; }
public string Name { get; set; }
public Options.Sizes Size { get; set; }
}
Somehow the Size property of Ball is always the first value in the dropdown, which is 'Small'. No matter what I pick.
If you inspect the dropdown in the browser, with your version you'll see it has the name Size.Size. This causes the binding not to work when you post to your Create action (assuming that's just expecting a Ball model, as you haven't included it here), because that is expecting something with the name Size.
It works for me when I change your EditorTemplate so the final line is:
#Html.DropDownListFor(m => m, items)
Using DropDownListFor ensures the generated select element gets the right name.
try removing .toString() from value.toString()

Html.DropDownList(listname, list) doesn't return a list with a selected item [duplicate]

I have tried this is RC1 and then upgraded to RC2 which did not resolve the issue.
// in my controller
ViewData["UserId"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
result: the SelectedValue property is set on the object
// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>
result: all expected options are rendered to the client, but the selected attribute is not set. The item in SelectedValue exists within the list, but the first item in the list is always defaulted to selected.
How should I be doing this?
Update
Thanks to John Feminella's reply I found out what the issue is. "UserId" is a property in the Model my View is strongly typed to. When Html.DropDownList("UserId" is changed to any other name but "UserId", the selected value is rendered correctly.
This results in the value not being bound to the model though.
This is how I fixed this problem:
I had the following:
Controller:
ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;
View
<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>
Changed by the following:
View
<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>
It appears that the DropDown must not have the same name has the ViewData name :S weird but it worked.
Try this:
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
And then:
var list = new[] {
new Person { Id = 1, Name = "Name1" },
new Person { Id = 2, Name = "Name2" },
new Person { Id = 3, Name = "Name3" }
};
var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;
Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])
With MVC RC2, I get:
<select id="PeopleClass" name="PeopleClass">
<option value="1">Name1</option>
<option selected="selected" value="2">Name2</option>
<option value="3">Name3</option>
</select>
You can still name the DropDown as "UserId" and still have model binding working correctly for you.
The only requirement for this to work is that the ViewData key that contains the SelectList does not have the same name as the Model property that you want to bind. In your specific case this would be:
// in my controller
ViewData["Users"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>
This will produce a select element that is named UserId, which has the same name as the UserId property in your model and therefore the model binder will set it with the value selected in the html's select element generated by the Html.DropDownList helper.
I'm not sure why that particular Html.DropDownList constructor won't select the value specified in the SelectList when you put the select list in the ViewData with a key equal to the property name. I suspect it has something to do with how the DropDownList helper is used in other scenarios, where the convention is that you do have a SelectList in the ViewData with the same name as the property in your model. This will work correctly:
// in my controller
ViewData["UserId"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
// in my view
<%=Html.DropDownList("UserId")%>
The code in the previous MVC 3 post does not work but it is a good start. I will fix it. I have tested this code and it works in MVC 3 Razor C# This code uses the ViewModel pattern to populate a property that returns a List<SelectListItem>.
The Model class
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
The ViewModel class
using System.Web.Mvc;
public class ProductListviewModel
{
public List<SelectListItem> Products { get; set; }
}
The Controller Method
public ViewResult List()
{
var productList = new List<SelectListItem>();
foreach (Product p in Products)
{
productList.Add(new SelectListItem
{
Value = p.ProductId.ToString(),
Text = "Product: " + p.Name + " " + p.Price.ToString(),
// To set the selected item use the following code
// Note: you should not set every item to selected
Selected = true
});
}
ProductListViewModel productListVM = new ProductListViewModeld();
productListVM.Products = productList;
return View(productListVM);
}
The view
#model MvcApp.ViewModels.ProductListViewModel
#using (Html.BeginForm())
{
#Html.DropDownList("Products", Model.Products)
}
The HTML output will be something like
<select id="Products" name="Products">
<option value="3">Product: Widget 10.00</option>
<option value="4">Product: Gadget 5.95</option>
</select>
depending on how you format the output. I hope this helps. The code does work.
If we don't think this is a bug the team should fix, at lease MSDN should improve the document. The confusing really comes from the poor document of this. In MSDN, it explains the parameters name as,
Type: System.String
The name of the form field to return.
This just means the final html it generates will use that parameter as the name of the select input. But, it actually means more than that.
I guess the designer assumes that user will use a view model to display the dropdownlist, also will use post back to the same view model. But in a lot cases, we don't really follow that assumption.
Use the example above,
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
If we follow the assumption,we should define a view model for this dropdownlist related view
public class PersonsSelectViewModel{
public string SelectedPersonId,
public List<SelectListItem> Persons;
}
Because when post back, only the selected value will post back, so it assume it should post back to the model's property SelectedPersonId, which means Html.DropDownList's first parameter name should be 'SelectedPersonId'. So, the designer thinks that when display the model view in the view, the model's property SelectedPersonId should hold the default value of that dropdown list. Even thought your List<SelectListItem> Persons already set the Selected flag to indicate which one is selected/default, the tml.DropDownList will actually ignore that and rebuild it's own IEnumerable<SelectListItem> and set the default/selected item based on the name.
Here is the code from asp.net mvc
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
...
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
if (defaultValue != null)
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
So, the code actually went further, it not only try to look up the name in the model, but also in the viewdata, as soon as it finds one, it will rebuild the selectList and ignore your original Selected.
The problem is, in a lot of cases, we don't really use it that way. we just want to throw in a selectList with one/multiple item(s) Selected set true.
Of course the solution is simple, use a name that not in the model nor in the viewdata. When it can not find a match, it will use the original selectList and the original Selected will take affect.
But i still think mvc should improve it by add one more condition
if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
Because, if the original selectList has already had one Selected, why would you ignore that?
Just my thoughts.
This appears to be a bug in the SelectExtensions class as it will only check the ViewData rather than the model for the selected item. So the trick is to copy the selected item from the model into the ViewData collection under the name of the property.
This is taken from the answer I gave on the MVC forums, I also have a more complete answer in a blog post that uses Kazi's DropDownList attribute...
Given a model
public class ArticleType
{
public Guid Id { get; set; }
public string Description { get; set; }
}
public class Article
{
public Guid Id { get; set; }
public string Name { get; set; }
public ArticleType { get; set; }
}
and a basic view model of
public class ArticleModel
{
public Guid Id { get; set; }
public string Name { get; set; }
[UIHint("DropDownList")]
public Guid ArticleType { get; set; }
}
Then we write a DropDownList editor template as follows..
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
IEnumerable<SelectListItem> GetSelectList()
{
var metaData = ViewData.ModelMetadata;
if (metaData == null)
{
return null;
}
var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
ViewData[metaData.PropertyName] = selected;
var key = metaData.PropertyName + "List";
return (IEnumerable<SelectListItem>)ViewData[key];
}
</script>
<%= Html.DropDownList(null, GetSelectList()) %>
This will also work if you change ArticleType in the view model to a SelectListItem, though you do have to implement a type converter as per Kazi's blog and register it to force the binder to treat this as a simple type.
In your controller we then have...
public ArticleController
{
...
public ActionResult Edit(int id)
{
var entity = repository.FindOne<Article>(id);
var model = builder.Convert<ArticleModel>(entity);
var types = repository.FindAll<ArticleTypes>();
ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);
return VIew(model);
}
...
}
The problems is that dropboxes don't work the same as listboxes, at least the way ASP.NET MVC2 design expects: A dropbox allows only zero or one values, as listboxes can have a multiple value selection. So, being strict with HTML, that value shouldn't be in the option list as "selected" flag, but in the input itself.
See the following example:
<select id="combo" name="combo" value="id2">
<option value="id1">This is option 1</option>
<option value="id2" selected="selected">This is option 2</option>
<option value="id3">This is option 3</option>
</select>
<select id="listbox" name="listbox" multiple>
<option value="id1">This is option 1</option>
<option value="id2" selected="selected">This is option 2</option>
<option value="id3">This is option 3</option>
</select>
The combo has the option selected, but also has its value attribute set. So, if you want ASP.NET MVC2 to render a dropbox and also have a specific value selected (i.e., default values, etc.), you should give it a value in the rendering, like this:
// in my view
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { #Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>
In ASP.NET MVC 3 you can simply add your list to ViewData...
var options = new List<SelectListItem>();
options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });
ViewData["options"] = options;
...and then reference it by name in your razor view...
#Html.DropDownList("options")
You don't have to manually "use" the list in the DropDownList call. Doing it this way correctly set the selected value for me too.
Disclaimer:
Haven't tried this with the web forms view engine, but it should work too.
I haven't tested this in the v1 and v2, but it might work.
I managed to get the desired result, but with a slightly different approach. In the Dropdownlist i used the Model and then referenced it. Not sure if this was what you were looking for.
#Html.DropDownList("Example", new SelectList(Model.FeeStructures, "Id", "NameOfFeeStructure", Model.Matters.FeeStructures))
Model.Matters.FeeStructures in above is my id, which could be your value of the item that should be selected.

Failing getting checkboxes values on the controller

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;
}