DropDownListFor enum doesn't bind back to the model - asp.net-mvc

On my MVC form, I need to bind a drop down box to an enumeration on my ViewModel. The best way I found to do that is described here.
It appeared to work at first, but now that I've added validation to my form, I've discovered it does not bind back to the ViewModel.
Here's my razor code:
<div class="editor-field">
#Html.DropDownListFor(model => model.response,
new SelectList(Enum.GetValues(typeof(Vouchers.Models.ResponseType))),
"Please Select")
</div>
And here's my view model definition for the field:
[DisplayName("Response")]
[Range(1, int.MaxValue, ErrorMessage = "You must select a response before submitting this form.")]
public ResponseType response { get; set; }
The problem is that I cannot submit the form; even after selecting a response from my drop down, the Validation error message for the Range attribute is displayed and the client side validation prevents the form from submitting.
I believe this is because the SelectList for the drop down contains only the string names of the enum, not the underlying integer value.
How can I solve this problem?

Create Dictionary where Key will be integer representation of the enum and string- the name of enum.
#Html.DropDownListFor(model => model.response,
new SelectList(Enum.GetValues(typeof(Vouchers.Models.ResponseType)).OfType<Vouchers.Models.VoucherResponseType>().ToDictionary(x => Convert.ToInt32(x), y => y.ToString()), "Key", "Value"), "Please Select")
Sorry for possible errors, I have not tried it.

Related

Required Validation Message MVC

I need to add a validation control in ASP MVC for a textbox. I used the #Html.ValidationMessage helper.
There is one textbox and I want to show the error If the value is not entered in that field.
I used the following code.
#Html.TextBox("txtStudentNumber", "4234234");
#Html.ValidationMessage("txtStudentNumber", "Please enter the student number", new { #class = "text-danger" })
The above message is always shown irrespective if the textbox has a value or not.
I need to validate if the student textbox is empty and also when the save button is clicked.
First of all, you have to use strongly types controls. i.e you should use TextBoxFor instead of TextBox and use ValidationMessageFor instead of ValidationMessage.
and then you have to bind the textbox with model. Try this :
Step 1: Create one model class "ClsStudent"
public class ClsStudent{public int studentNo {get;set;}}
Step 2: Bind the model student number in the view :
#model ClsStudent
#Html.TextBoxFor(m => m.studentNo)
#Html.ValidationMessageFor(m => m.studentNo, null, new { #class = "error" })
Validation on Submit Button :
Submit

ASP.NET MVC 5 model validation for non-nullable types (Int32)

I'm working on an ASP.NET MVC 5 application and the project owner is concerned about "under-posting" issues caused by validating non-nullable types (as mentioned in http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html and http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api).
I created a test case to replicate this issue in ASP.NET MVC 5 but without luck.
Model:
public class ContactModel
{
[Required]
public Int32 data1 { get; set; }
public Int32 data2 { get; set; }
}
View:
<div class="form-group">
#Html.LabelFor(model => model.data1)
<div>
#Html.EditorFor(model => model.data1)
</div>
</div>
<div>
#Html.LabelFor(model => model.data2)
<div>
#Html.EditorFor(model => model.data2)
</div>
</div>
Controller:
public ActionResult Index(Models.ContactModel contact)
{
if (ModelState.IsValid)
{
Response.Write("modelstate is valid<br>");
return View();
}
else
{
Response.Write("modelstate is invalid<br>");
return View();
}
}
It seems that when data1 and data2 are null in the post, their values in the model (contact) will be 0. However, ModelState.IsValid will also be false (instead of true as shown in the two articles).
What I have:
What the second article showed:
I couldn't find any information regarding changes on how model validation works in ASP.NET MVC, so I'm guessing I did something wrong with my test case. Any thought and suggestion are appreciated.
The reason your ModelState is false is because the post is providing form values from each property in your model. Essentially the Model binding system is checking the validity of both data1 and data2 fields as you have #Html.EditorFor helpers explicitly written for both properties in your view (so no underposting is actually going on).
I did successfully replicate the under-posting concerns from the articles. Simply remove one of the EditorFor helpers in your view, so you're actually underposting. With both helpers present, there's no underposting going on. So the view looks like this now (note I added the validation helper for both properties to get feedback in the view on what's going on):
View:
<div class="form-group">
#Html.LabelFor(model => model.data1)
<div>
#Html.EditorFor(model => model.data1)
#Html.ValidationMessageFor(model => model.data1)
#Html.ValidationMessageFor(model => model.data2)
</div>
</div>
Make sure to leave the #Html.EditorFor helper completely off for the data2 property. Now fill in zero in the form field (you'll only one form field in the view of course now), and post to your action.
ModelState will come back as true in this scenario, even though only one form field is being posted. Not a good result if someone does underpost! So here's the (slightly modified) original model class where underposting issues will occur in the case a form field is left off of your form (note the Required attributes don't make any difference in this situation as both properties are value types):
//You could add the Required attribute or not, doesn't matter at this point.
//The concern here is that the Modelstate will still come back as Valid
//in the case of a form field being left off of your form (or someone underposts).
//So to replicate underposting issues, make sure to comment or delete
//at least one Html.EditorFor helper in the view.
//[Required] Underposting will occur regardless if this is marked required or not,
//so be careful if someone does underpost your form.
public Int32 data1 { get; set; }
//[Required]
public Int32 data2 { get; set; }
Now the solution if you want to solve the underposting issue:
Simply mark both properties as required and make them nullable as mentioned in the articles you provided, like so:
[Required]
public Int32? data1 { get; set; }
[Required]
public Int32? data2 { get; set; }
Now when the view is posted with a missing #Html.EditorFor helper or a missing form field, the ModelState Validation will come back as false, and you're protected from underposting issues.

How to FluentBootstrap validation in MVC

Is it possible to use validation messages in FluentBootstrap on form element level?
When I use:
#using (var form = Html.Bootstrap().Form().Begin())
{
#form.InputFor(m => m.Name)
#form.Submit()
}
There is no validation, except validation error class on input tag. I want to show validation message for Name property and has-error class on form-group container.
Just add #form.ValidationSummary() where you want the general validation summary to appear. There is an example of this as well as some other examples here: https://github.com/daveaglick/FluentBootstrap/blob/develop/FluentBootstrap.Tests.Web/Views/MvcTests/MvcForms.cshtml
If there is no particular reason to use InpuFor you can do the following.
#form.EditorFor(m => m.Name, addValidationMessage: true)
This will show what error message you put on your Name property's attribute.
Such as
[StringLength(50, ErrorMessage="Cannot be longer than 50 character.")]
public string Name { get; set; }
I personally use the base validation helper
#form.InputFor(m => m.Password, FormInputType.Password)
#Html.ValidationMessageFor(m => m.Password)
I can set the type of the input unlike the case when I use EditorFor

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

Why does my MVC dropdownlist have a value selected despite the property value being NULL?

I have a dropdownlist where a user can select a care provider. In the database the value for this is null however in the application, a value is selected. What do I need to do to have the null value appear as a blank? I thought this was the default behavior. I changed to using strongly typed lists in my view model instead of the viewbag and this may have broken at that time.
Here is the view markup:
<div class="editor-label">
#Html.LabelFor(model => model.PsychologistId, "Psychologist")
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.PsychologistId, Model.ListPsychologists)
#Html.ValidationMessageFor(model => model.PsychologistId)
</div>
Here is the property from the view model:
[DisplayName("Psychologist")]
public Nullable<int> PsychologistId { get; set; }
Here is the relevant part of my controller:
model.ListPsychologists = new SelectList(XXX, "Id", "DisplayName");
return this.View(model);
where XXX is just the LINQ expression with filtering and sorting criteria. It's been omitted for clarity and space.
The model passed from the controller to the view has PsychologistId being null. And the SelectedValue property on model.ListPsychologists is null.
if PsychologistId is an int, it will assign 0 value to it since int is not a nullable type.
Show your model and controller if my assumption above is not true.
Does your SelectList contain entry with Id==null? I doubt that. And that means that you will have no select list entry matching your value, so browser will select first one available.
Solution: explicitly add entry with Id = null to your SelectList.
I've had these issues before, and what I did to fix it was send the base collection of what I want to fill the dropdown with in the viewmodel, instead of a SelectList. So:
model.ListPsychologists = XXX;
Then in the view:
#Html.DropDownListFor(x => x.PsychologistId, new SelectList(Model.ListPsychologists, "Id", "DisplayName", Model.PsychologistId))
Please verify the SelectList constructor overload I used in MSDN, I'm typing this from memory. Basically you give it the collection of items to use in the SelectList, then the name of the text and value properties (just like you did in your action method code), and the fourth parameter is the value to set the drop down to.
Edit: Also, there is an overload for the DropDownListFor to add a default item in the menu. Like, "Select One".

Resources