Validation is only applied to the first item of an array - c#

Given this model code
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
the following view code works
#Html.LabelFor(model => model.Name)
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
Whenever I leave the Name field empty, the TextBox is highlighted in red, and a "The Name field is required." error message appears, which is good.
However, my model also contains an Option[] Options which also need to be validated.
Given this model code
private const string orderNoDisplayName = "Order number";
[Required]
[RegularExpression(#"\d+",
ErrorMessage = "The " + orderNoDisplayName + " field must be numeric.")]
[Display(Name = orderNoDisplayName )]
public string OrderNo { get; set; }
the following view code doesn't quite work as expected
#foreach (var option in Option.GetDefaultOptions())
{
<li>
#Html.TextBoxFor(model => option.OrderNo, new { id = option.IdString })
#Html.ValidationMessageFor(model => option.OrderNo,
null, new { data_valmsg_for = option.IdString })
</li>
}
The very first option works perfectly, but any subsequent option doesn't.
Below is the automatically generated code for 2 options
<ul>
<li> <!-- automatically generated code for 'option_1' -->
<input data-val="true" data-val-regex="The Order number field must be numeric."
data-val-regex-pattern="\d+" data-val-required="The Order number field is required."
id="option_1" name="OrderNo" type="text" value="0" class="input-validation-error">
<span class="field-validation-valid" data-valmsg-for="option_1"
data-valmsg-replace="true"></span>
</li>
<li> <!-- automatically generated code for 'option_2' -->
<input id="option_2" name="OrderNo" type="text" value="0"
class="input-validation-error">
<span class="field-validation-valid" data-valmsg-for="option_2"
data-valmsg-replace="true"></span>
</li>
</ul>
So obviously, MVC did not add any validation attributes to my second option or any subsequent options at all.
Of course, I could hardcode the validations into a handwritten <input> tag, but I'd like to avoid doing so. What can I do to make the validation work for all options, instead of only the first one?

I think the problem is the for loop, as the model binder is not indexing the name field they're coming out identical and it looks like there is only one i.e. name="OrderNo".
Try changing your loop to index it as follows:
#for (var i = 0; i < Option.GetDefaultOptions().Count; i++)
{
<li>
#Html.TextBoxFor(model => Option.GetDefaultOptions()[i].OrderNo, new { id = Option.GetDefaultOptions()[i].IdString })
#Html.ValidationMessageFor(model => Option.GetDefaultOptions()[i].OrderNo,
null, new { data_valmsg_for = Option.GetDefaultOptions()[i].OrderNo.IdString })
</li>
}
You should then see that they're written out as follows:
name="[0].OrderNo"
name="[1].OrderNo"

Related

MVC Model validation on lose focus

Is it possible to change when client side validation happens when using MVC data annotations? Currently, all validation seems to occure on keyup, but I need it to occure onchange or losefocus.
ViewModel:
[Display(Name = "First Name")]
[Required(ErrorMessage = "First Name is Required")]
public string FirstName { get; set; }
cshtml
#Html.TextBoxFor(m => m.FirstName, new { placeholder = Html.DisplayNameFor(n => n.FirstName) })
#Html.ValidationMessageFor(m => m.FirstName)
Rendered HTML
<div class="field">
<input data-val="true"
data-val-required="First Name is Required"
id="FirstName"
name="FirstName"
placeholder="First Name"
type="text"
value="">
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
</div>
This works as it should, but when I type into input, validation occurs as I type. How can I make validation occure after I've clicked or tabbed off the input?
Versions
.NET 4.6.1
Microsoft.Aspnet.Mvc 5.2.3
Microsoft.jQuery.Unobtrusive.Validation 3.2.3
I was able to use Javascript to get the desired behavior.
$.validator.setDefaults({
onkeyup: false
})
All validation now happens on blur

Rendering bootstrap components in EditorTemplates

I have the following property in my QuoteSalesRep class:
[Display(Name = "Commision %")]
[UIHint("Percentage")]
public decimal CommisionPercentage { get; set; }
I would like to create an Editor template that would render anything with the [UIHint("Percentage")] like so:
Under Views/Shared/EditorTemplates I added Percentage.cshtml. I currently have the following code:
#model decimal
<div class="input-group">
<input type="text" class="form-control">
<span class="input-group-addon">%</span>
</div>
I am new to working with templates. What do I need to do to correct the above code so it renders the input-group properly for any property tagged with [UIHint("Percentage")]?
I think all you're missing is a generic way of generating the text input:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue.ToString(), new { #class = "form-control" })

How to use a required data annotation for a list of textboxes

I have a form with multiple values for a custom question. The idea being a user can make their own custom question with custom answers. One of the properties of the Model object is a list of objects:
public class CustomQuestion
{
public int Id { get; set; }
[Required]
public string QuestionName{ get; set; }
public virtual List<CustomQuestionAnswer> Answers { get; set; }
}
public class CustomQuestionAnswer
{
public int Id { get; set; }
[Required]
public string Answer { get; set; }
}
Now I have this in razor which works to create the answers (I have omitted the rest of the question):
foreach (var answer in Model.Answers)
{
if (answer.IsActive)
{
<div class="form-group answer">
<div class="col-md-10">
#Html.Hidden("answers[" + count + "].Id", answer.Id)
#Html.TextBox("answers[" + count + "].Answer", answer.Answer, new { #class = "form-control" })
validation code will go here
</div>
<div class="col-md-2">
#Html.CheckBox("answers[" + count + "].IsActive", answer.IsActive)
</div>
</div>
count++;
}
}
Now everything is working fine and saving to the database, except the data validation is not working. I have the data validation messages working for the CustomQuestion's QuestionName (so I know it is not an issue of jquery loading properly), but not for the answers. I have tried putting these lines in the razor form:
#Html.ValidationMessageFor(m => answer.Answer, "", new { #class = "text-danger" })
#Html.ValidationMessage("Answers[" + count + "].Answer", "", new { #class = "text-danger" })
But neither works. Also, when I click on the save button, I get this error message in the browser console:
Failed to load resource: the server responded with a status of 400 ({"Answers[0].Answer":["The Answer field is required."]})
Any ideas as to what I need for the validation code?
If your model is (or contains) an indexed collection, it is better to use a for-loop along with fully specified variables and with ...For helpers, like this:
for (var i = 0; i < Model.Answers.Count; i++)
{
if (Model.Answers[i].IsActive)
{
<div class="form-group answer">
<div class="col-md-10">
#Html.HiddenFor(m => Model.Answers[i].Id)
#Html.TextBoxFor(m => Model.Answers[i].Answer, new { #class = "form-control" })
#Html.ValidationMessageFor(m => Model.Answers[i].Answer, null, new { #class = "text-danger" })
</div>
<div class="col-md-2">
#Html.CheckBoxFor(m => Model.Answers[i].IsActive)
</div>
</div>
}
}
It may seem awkward or even unnecessary to keep referencing the full Model.Answers[i] everywhere, but MVC uses reflection on these expressions to come up with the full names to output, which are needed for Model Binding to succeed, and also for pairing it with ModelState which uses the same object structure.
The use of a temporary or helper variable such as your answer will cause MVC to not be able to do this anymore, and then the burden becomes your own.
You should use strongly typed helpers if you have ViewModel. To make right binging you should use for loop instead of foreach.
Like this:
for (int i = 0; i < Model.Answers.Count(); i++)
{
if (Model.Answers[i].IsActive)
{
<div class="form-group answer">
<div class="col-md-10">
#Html.HiddenFor(x => Model.Answers[i].Id)
#Html.TextBoxFor(x => Model.Answers[i].Answer, new { #class = "form-control" })
#Html.ValidationMessageFor(m => x => Model.Answers[i].Answer, null, new { #class = "text-danger" })
</div>
<div class="col-md-2">
#Html.CheckBox(x => Model.Answers[i].IsActive)
</div>
</div>
}
}
you can customize you message in the model
like:
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "You need a valid email adress")]
[MaxLength(40, ErrorMessage = "the length of an Email address must be at least 40 chars")]
[Display(Name = "Email")]
public string CurrentMail { get; set; }
and then you make a simple
#Html.TextBoxFor(m => m.CurrentMail)
instead Of #Html.TextBox(m => m.CurrentMail)
For Validation you can use this code :
#Html.ValidationMessageFor(m => answer.Answer,null, new { #class = "text-danger" })
Please check it and let me know..

ASP.NET MVC Client Side Validation Message Error

Can somebody tell me why "This field is required" and "Please insert database name" are being displayed instead of just "Please insert database name"?
This is my model :
public class InstallViewModel
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Please insert database name")]
public string DatabaseName { get; set; }
and this is my view :
<div class="input-group">
<span class="input-group-addon">Database</span>
#Html.TextBoxFor(w => w.DatabaseName, new { #class = "form-control", placeholder = "Database name" })
</div>
#Html.ValidationMessageFor(w=> w.DatabaseName)
Thank you.
EDIT:
Can you see the image attached ? I have some problems uploading images.
The view is a partial view and this is the whole partial view:
#Html.ValidationMessageFor(w => w.DatabaseName)
<div class="input-group">
<span class="input-group-addon">Database</span>
#Html.TextBoxFor(w => w.DatabaseName, new { #class = "form-control", placeholder = "Database name" })
</div>
<br />
#Html.CheckBoxFor(w => w.UseWindowsAuthentication, new { #checked = "checked" }) Use Windows Authentication<br /><br />
<div class="wizard-sqlauth" style="display: none">
<div class="input-group">
<span class="input-group-addon">User name</span>
#Html.TextBoxFor(w => w.UserName, new { #class = "form-control", placeholder = "User name" })
</div>
#Html.ValidationMessageFor(w => w.UserName)<br />
<div class="input-group">
<span class="input-group-addon">Password</span>
#Html.PasswordFor(w => w.Password, new { #class = "form-control" })
</div>
#Html.ValidationMessageFor(w => w.Password)
</div>
DatabaseName is "Required" and your input is empty. (There is only placeholder text)
Are you calling jquery validation "manually" anywhere in javascript, i.e.
$('#myform').valid() ?
That would trigger the default value for the required rule ("This field is required."), and would append it as a label after the input, which is exactly the behavior your are experiencing.
If you really need to use both (MVC's Unobstrusive validation + jQuery validation) you can configure jquery validation to ignore certain fields, for example
$('#myform').validate({
ignore: '#databasefieldId'
});
You have applied the RequiredAttribute attribute to a property to the property DatabaseName which implies that the property must contain a value.
A validation exception is raised if the property is null, an empty string (""), or contains only white-space characters.
You just add #Html.ValidationMessageFor(w=> w.DatabaseName) in the top of div. This will show the summary.

Auto Tooltip Validation in MVC 4?

Where the heck are these things coming from? I like them, and I would like to leverage them elsewhere in my site. It appears they only show when I do regular expression validation in model:
[Display(Name = "Residential")]
[RegularExpression(#"[-+]?[0-9]*\.?[0-9]?[0-9]", ErrorMessage = "Must be a number")]
public Byte? residentialExperience { get; set; }
<div class="editor-label row">
#Html.LabelFor(model => model.residentialExperience)
</div>
<div class="editor-field row">
#Html.EditorFor(model => model.residentialExperience)
#Html.ValidationMessageFor(model => model.residentialExperience)
</div>
How can I use these validation tooltips elsewhere? Also, how can I turn them off?
Also: It's not displaying the same message as I have in my model. It says, "Please enter a number" whereas I have written "Must be a number."
This is because you are outputting a numeric field. If you look at your HTML you will see that you have something like this:
<input type="number" ... />
By defining the type as a numbber, the browser knows what to expect and it will give you a generic message. This is part of Html 5 spec.
If you want to override the default behavior you could do this:
#Html.TextBoxFor(model => model.residentialExperience, new { #type = "text" })

Categories