ASP.NET MVC Html.RadioButton Exception - asp.net-mvc

I haver a simple radio button list on my page that I render with the following in my view:
<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender") %>
Note that when the user initially sees this input, neither button is selected.
The validation is there to force them to choose and not accept a default.
Therefore, these two radio buttons are bound to a nullable int property in my model declared as:
public int? gender { get; set; }
So if they do not select a button, and submit the page, the gender property will be null indicating that they did not select. The following validation is called by the controller during the post:
if (!gender.HasValue)
ModelState.AddModelError("gender", "gender required");
But, if the validation fails (they did not choose), then during the rendering phase, the following exception is thrown by the MVC framework:
System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
In searching for for a solution to this problem, I noted several had this problem.
I am using ASP.NET MVC 1.0. I found the place in the code where this error is thrown using .NET Reflector.
The question is how to make this work correctly?
EDIT: to add stacktrace:
System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
Source="System.Web.Mvc"
StackTrace:
at System.Web.Mvc.HtmlHelper.GetModelStateValue(String key, Type destinationType)
at System.Web.Mvc.Html.InputExtensions.InputHelper(HtmlHelper htmlHelper, InputType inputType, String name, Object value, Boolean useViewData, Boolean isChecked, Boolean setId, Boolean isExplicitValue, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, Boolean isChecked, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value)
at ASP.views_vbs_register_aspx.__RenderregisterContent(HtmlTextWriter __w, Control parameterContainer) in c:\Users\David\Documents\BellevueProject\Bellevue\BellevueTeachers\Forms\Views\VBS\Register.aspx:line 42

I just tried something that makes this work.
The problem does not occur if I do not do the validation step but of course I need the validation.
That gave me a clue for the solution.
The ValidationMessage HtmlHelper method takes a string argument that is the name of the property or model object being validated.
I just changed that name to be "gender2" as follows:
<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender2") %>
And I changed the validation code to refer to this new name (even though that property does not exist, it still works):
if (!gender.HasValue)
ModelState.AddModelError("gender2", "gender required");
This works as desired.
I would have thought the other should have worked, but this is a simple workaround and I am documenting that here.
EDIT:
By the way I tried changing the gender property to a string instead of a nullable int, and the same exact problem occurs.
The work around still seems to be in using a different key name for the Validation Message.

This is very similar, if not equal to the checkbox issue: The Html.Checkbox() helper method generates a hidden field with a false value. If this field were missing, the browser would not send any data for uncheked boxes.
A radiobutton, however, is supposed to have a value, and the possible values can be more than one. In this case it is not so easy to handle the non-selection case, which I guess is the reason it isn't.
The workaround for me was to add a hidden field as follows:
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.Hidden("gender", null) %>

You might want to try changing gender to a string (M/F) instead of an int and see if that works.
If you absolutely must have it as an int, you could always translate on the back end.
private int? gender { get; set; }
public string displayGender
{
get
{
return this.gender.HasValue
? (this.gender.Value == 1 ? "M" : "F" )
: null;
}
set
{
this.gender = null;
if (value == "M")
this.gender = 1;
else if (value == "F")
this.gender = 2;
}
}
<label for="gender">Gender</label>
<%= Html.RadioButton("displayGender", "M") %> Male
<%= Html.RadioButton("displayGender", "F") %> Female
<%= Html.ValidationMessage("displayGender") %>
Base on your comment, you may want to add:
<%= Html.RadioButton("displayGender",
string.Empty,
true, // this is the default
new { #style = "display: none;" } ) %>
This will ensure that displayGender gets posted back (there will always be a chosen radio) and I think the value will be string.Empty instead of a null reference. If this works, you may want to try switching back to the nullable int.

Try adding the following line of code after the ModelState.AddModelError():
ModelState.SetModelValue("gender", ValueProvider["gender"]);

If you want to wire in client-side validation. Skip the html helper and hand-write your radios, giving them attributes for data-val and data-val-required. Like this.
<input type="radio" name="displayGender" id="displayGender" value="1" data-val="true" data-val-required ="The gender field is required."/>
<input type="radio" name="displayGender" id="displayGender" value="2" data-val="true" data-val-required ="The gender field is required."/>

Related

Validation error - The field Int32 must be a number - on string property

I have a funny problem and I did not find the cause.
In asp.net MVC application I have a form that saves some simple information. All fields can be stored neatly except one. This field (string) returns validation error "The Int32 field must be a number" but only if the sentences in the text contains a single digit number. For example, if the sentence is as follows:
"Some simple sentence that contains the number 3" I'll get a validation error - "The Int32 field must be a number", if that same sentence transformed into:
"Some simple sentence that contains the number 30" or "Some simple sentence that contains a 30%" - no errors
Field property from a model:
[Display(Name = "Some name")]
[StringLength(500, ErrorMessage = "You can enter up to 500 characters.")]
public string Akcija { get; set; }
Next to that field I have one (string) field with the same property characteristics, and it is working properly.
Clip from view:
<div>
#Html.TextAreaFor(m => m.Akcija, new { style = "width:500px; height:100px;" })
#Html.ValidationMessageFor(m => m.Akcija)
</div>
It can not be simpler than that, but the problem is around here.
Do you have any suggestions?
Edit:
If I keep trying to save the changes it will be saved to the database regardless of the validation error. It seems to me that this is a JavaScript validation error or a bug
Edit 2 - Generated HTML:
<textarea cols="20" data-val="true" data-val-number="The field Int32 must be a number." data-val-required="The Int32 field is required." id="Akcija" name="Akcija" rows="2" style="width:500px; height:100px;">
Web aplikacije i siteovi 3 sa 30% sniženja
I do not know where it comes from - this attribute "data-val-number =" The field Int32 must be a number. '" And " data-val-required = "The Int32 field is required.'"
I'm not in the code imposed these rules
Try tagging the property with [DataType(DataType.MultilineText)]. Maybe that will make it explicit to the TextAreaFor helper.
If anyone ever run into this problem, the workaround is to use Html.EditorFor how it follows:
First: Add validation attribute [DataType (DataType.MultilineText)] to model property
so that model property looks like this:
[Display(Name = "Some name")]
[StringLength(500, ErrorMessage = "You can enter up to 500 characters.")]
[DataType(DataType.MultilineText)]
public string Akcija { get; set; }
Secondly: Make Template for Html.EditorFor.
Template create in a folder Views/Shared/EditorTemplates - if there is no folder EditorTemplates, make it.
In the folder EditorTemplates create a partial view named with the name of the model property.
In my case it is: akcija.cshtml
In this view should be forwarded the value of the model property and you must include the HTML that defines the field of view so that the template, in my case, look like this:
#model System.String
#if(!string.IsNullOrEmpty(Model))
{
<textarea name="Akcija" style="width:500px;height:100px;">#Model.ToString() </textarea>
}
else
{
<textarea name="Akcija" style="width:500px;height:100px;">#Model</textarea>
}
Third In your View change Html.TextAreaFor in Html.EditorFor and put a reference to your template. Html.EditorFor, in my case, now looks like this:
<div>
#Html.EditorFor(m => m.Akcija, "akcija")
#Html.ValidationMessageFor(m => m.Akcija)
</div>
And that's it, problem solved.
Note: When naming template and objects within the template and the template it self, note the naming convention. Note that my template is named with small letter and in html template there is no id attribute for html object.
I hope that this will help someone
Use a nullable int/Int32.
public int? mobilequantity { get; set; }
or
public Int32? mobilequantity {get; set;}
that works on my form

Hiddenfor not getting correct value from view model

I have a multi-step file import process. I have a hidden form input in my view that I am trying to populate with the "CurrentStep" from the view model.
<% = Html.HiddenFor(model => model.CurrentStep) %>
CurrentStep is an Enum and I always get the default value rather than the one I provided to the view model. on the other hand this gets me the correct value:
<p><% = Model.CurrentStep %></p>
I realise I could just hand code the hidden input but I want to know: what am I doing wrong? Is there a better way to keep track of the current step between POSTs?
What you are doing wrong is that you are trying to modify the value of a POSTed variable in your controller action. So I suppose you are trying to do this:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
and html helpers such as HiddenFor will always first use the POSTed value and after that the value in the model.
So you have a couple of possibilities:
Remove the value from the modelstate:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
ModelState.Remove("CurrentStep");
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
Manually generate the hidden field
<input type="hidden" name="NextStep" value="<%= Model.CurrentStep %>" />
Write a custom helper which will use the value of your model and not the one that's being POSTed
My solution was to use Darin's second option, because option 1 (clearing from the model state) means hard coding a string (and the naming convention can be tricky with complex models), and wanted to avoid option 3 because I already have so many custom helpers.
<input type="hidden" name="#Html.NameFor(x => Model.SomeId)" value="#Model.SomeId" />
Just a reminder that you can use Html.NameFor to keep things clean.
Make sure you model property has a "set" operator.
This won't get updated on post-back:
#Html.HiddenFor( m => m.NoSeq)
public Class MyModel
{
int _NoSeq;
public NoSeq
{
get { return _NoSeq };
}
}

What's the point of Html.DisplayTextFor()?

Is there a good reason to use the strongly typed html helper...
<%: Html.DisplayTextFor(model => model.Email) %>
As opposed to...
<%: Model.Email %>
Consider the following Model:
public class MyModel
{
public string Name { get; set; }
[DisplayFormat(NullDisplayText = "No value available!")]
public string Email { get; set; }
}
in my view:
<%= Html.DisplayTextFor(m => m.Email) %>
<%: Model.Email %>
The first line will display "No value available" if we leave the Email to be 'null' whereas the second line will not display anything.
Conclusion:
Html.DisplayTextFor will take into consideration the DataAnnotations on your properties, <%: Model.Email %> will not.
Also <%: Model.Email %> will throw an "Object reference error" when the value is null, but <%= Html.DisplayTextFor %> wont.
DisplayTextFor will also get called during "DisplayFor" and "EditFor" execution. This will ensure that any templated helpers will display the text using the correct Templated Helpers if set ... so a change to the single templated helper will propogate through all displays of that text item ... simple display, edit form, create forms etc etc.
Well, DisplayTextFor will not break if you do not pass in a model (or pass null). Model.Email will throw an exception on null. So, DisplayTextFor is more robust.
The point of the strongly typed helpers is to allow greater compile time checking. This is very handy when refactoring.
DisplayTextFor allows using a consistent construct (with the other strongly typed helpers) throughout the page. Some people might find that more appealing.
DisplayTextFor also allows you to pass the names of templates and fields as parameters.

The string was not identified as a valid Boolean string

I have a page where I list checkboxes named TimeRange1, TimeRange2.... TimeRange7, and I generate them in a for loop like this:
<% for (int currentDay = 1; currentDay <= 7; currentDay++)
......<%= Html.CheckBox("TimeRange" + currentDay.ToString(), false)%>
Works fine til I post the form, then I get a "The string was not identified as a valid Boolean string."
Apparently the problem is that I concatenate the name of the checkbox.
Is there some neat way of fixing this? I need to have them named 1-7. It's like a big schedule where you select which times should be available.
Try without helpers:
<% for (int currentDay = 1; currentDay <= 7; currentDay++) { %>
<input type="checkbox" name="TimeRange<%= currentDay.ToString() %>" />
<% } %>
Html Helpers get their values from :
ViewData.ModelState["controlName"].Value.RawValue.
value parameter passed to HTML helper method.
They also generate a hidden input field in addition to the checkbox. So depending on the context sometimes you may end up with markup like this:
<input type="checkbox" name="TimeRange1" value="SomeValue, false" />
and when you post the form the data binder will be unable to convert the value to a boolean type.
An addition to this ol' question - might save someone a headache. I had accidentally labeled a ViewBag property to the same name as a model property, and got the above mentioned error message (when getting the view though, not posting).
In essence I had set the following in the controller:
ViewBag.TermsAndConditions = "blah blah blah"
and in my model I had:
public bool TermsAndConditions { get; set; }
The line #Html.CheckBoxFor(m => m.TermsAndConditions) threw and error from the view.
Renaming either the ViewBag or model property fixed it, obviously.

HTML.CheckBox(string) evaluates to two HTML elements instead of one

The code
<%=Html.CheckBox("SendEmail") %>
evaluates to two HTML elements when it's rendered
<input id="SendEmail" name="SendEmail" type="checkbox" value="true" />
<input name="SendEmail" type="hidden" value="false" />
Is this by a bug? Or by design? If it's by design, why?
I was having some trouble this morning with retrieving selected values from a checkbox group in my MVC app. I sent this out to my team and thought I'd share it with everyone else.
When posting back values for checkboxes, the standard behaviour of all browsers is to completely leave out un-checked checkboxes from the post back. So if you have a checkbox that isn’t checked then nothing shows up for this in Request.Form. This is a fairly well-known phenomenon that most developers account for.
In ASP.Net MVC when you use Html.Checkbox, it attempts to get around this and ensure that you have a value posted back (in this case ‘false’) for un-checked checkboxes. This is done by adding a hidden field containing the value ‘false’.
Eg.
<%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
Produces the HTML
<input id="Sites" name="Sites" type="checkbox" value="1" /><input name="Sites" type="hidden" value="false" />
This is all good and well, until you attempt to use checkbox groups. That is more than one checkbox with the same name where the values are sent back in a single comma separated string.
MVC can split this string up for you automatically if you define the value as an array (string[] Sites).
Here’s the view code:
<% foreach(var s in Model) { %>
<li><%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
<label><%= s.Name %></label>
</li>
<% } %>
The appropriate controller action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int Id, string Name, string SubmissionUrl, string[] Sites)
Unfortunately, because the Html.Checkbox produces this hidden field value as well, the resulting array contains not only values for the selected checkboxes but also ‘false’ for every un-checked checkbox. You get an array that looks something like this:
[0] 'false'
[1] 'false'
[2] '110'
[3] '50'
[4] 'false'
Where 'false' is for checkboxes that are not selected, and integers are the values for the checkboxes that are selected.
This can throw your code out quite a bit if you have only integers for the values of your checkboxes and want to define the result as an array of integers, like so:
public ActionResult Edit(int Id, string Name, string SubmissionUrl, int[] Sites)
Which results in an exception being thrown because it can’t convert the string value ‘false’ to an integer.
The solution is very simple, just avoid Html.Checkbox and manually create the checkboxes in your view code like this:
<% foreach(var s in Model) { %>
<li><input type="checkbox" name="Sites" value="<%=s.Value%>" <% if (s.Selected) { %>checked="checked"<% } %> />
<label><%= s.Name %></label>
</li>
<% } %>
Hope this helps someone else!
When all else fails, read the source code. :) this is from HtmlHelper.cs:
// Render an additional <input type="hidden".../> for checkboxes. This
// addresses scenarios where unchecked checkboxes are not sent in the request.
// Sending a hidden input makes it possible to know that the checkbox was present
// on the page when the request was submitted.
I'm not exactly sure how useful that is, but at least you know the intention.
I think I found something on the web that is directly related to my question.

Resources