3

We have a model that is being displayed in a form on a partial view with model validations. We have a few fields like this:

[Required(ErrorMessage = "{0} cannot be empty")]
public string FirstName { get; set; }

[Required(ErrorMessage = "{0} cannot be empty")]
public string LastName { get; set; }

[RegularExpression(@"(\d{5})?", ErrorMessage = "Zip must be a 5 digit number or empty.")]
public string ZipCode { get; set; }

Then in the view we are displaying text boxes for those fields with validation messages for them like this:

<td>
    @Html.TextBoxFor(m => m.Officer.FirstName)
    @Html.ValidationMessageFor(m => m.Officer.FirstName)
</td>
<td>
    @Html.TextBoxFor(m => m.Officer.LastName)
    @Html.ValidationMessageFor(m => m.Officer.LastName)
</td>
<td>
    @Html.TextBoxFor(m => m.Officer.ZipCode)
    @Html.ValidationMessageFor(m => m.Officer.ZipCode)
</td>

The validations are working, but I am noticing something odd with the timing and I haven't been able to find any documentation as to why.

When filling out the form, if I leave FirstName or LastName blank, the validation messages don't appear until I submit the form, check if the model state is valid and return to the page.

However, if I edit zip code with an invalid entry, the validation message popups immeadiately, even if I haven't submitted the form.

Is there anyway to get them to process at the same time so that it doesn't appear weird to the user?

Evan Frisch
  • 1,334
  • 5
  • 22
  • 40
  • If you also add a regular expression to First name and last name what does that do? That's the only difference I'm seeing as to why the behaviors might be different seeing as how they are both strings and both text boxes. – KSib Apr 26 '17 at 15:33
  • Can you please post the rendered HTML for the form. Is there any jQuery et al validation going on? – Mad Myche Apr 26 '17 at 15:35
  • I could theoretically see that they don't do validation on empty `[Required]` inputs until submission because of screen readers, or people tabbing through the form. ("I'll fill that in after I do the other.") However, leaving an input with invalid data (ZipCode), isn't something that under normal circumstances would be returned to. – krillgar Apr 26 '17 at 15:57
  • @krillgar client side validation for required annotation happens after submit.. the advantage mainly is that the required properties are checked on the client for immediate feedback, and not having to go to the server – Grizzly Apr 26 '17 at 16:10
  • 1
    @KSib I tried changing them to Regular expressions and they did start working before submit, which now that we have BviLLe_Kid's answer, that seems to make sense. – Evan Frisch Apr 26 '17 at 16:46
  • @BviLLe_Kid Yes, that's what the OP noticed is happening. The advantage of doing a client side check aren't being debated. What OP is seeing is that the Zip Code is being validated on blur, while the check for required fields isn't happening until later. My comment is speculation on the reasoning as to **why** the required check doesn't happen until submission when the other happens immediately. – krillgar Apr 26 '17 at 16:54
  • I always recommend to not rely on built-in HTML helpers to draw your controls. Do it yourself and you will have total control. – Houssam Hamdan Apr 26 '17 at 16:57
  • The code you have shown works perfectly fine and you form **will not** be submitted and client side validation messages will be shown if the `FirstName` and/or `LastName` fields are empty. If its not working for you, then you have other code inteferring with the default behavior. Validation is 'lazy' which allows you to tab through controls without triggering validation. If you enter a value, tab out then tab back in and clear the contents, the message will be shown immediately. –  Apr 27 '17 at 03:48
  • Refer the section titled _A few things to look out for when playing around with the demo_ in the [documentation](https://jqueryvalidation.org/documentation/) –  Apr 27 '17 at 03:49

2 Answers2

3

ASP.NET MVC uses Data Annotations for both server-side and client-side validation.

In the view, you are using Razor.. when you render input fields using HTML Helper Methods (@Html.TextBoxFor for example), the razor view engine looks at the data annotations applied on the reference properties and adds additional attributes to the HTML markup.

However, only certain/standard data annotations work for client side and they are:

  • [Required] - you won't see the validation message if you leave it empty & before hitting submit.. the client-side performance is seen after clicking 'Submit' and the form not being sent to the server because the client already noticed that those fields are empty.. you can check this by right-clicking on the page where the form is, and clicking 'Inspect' then clicking the 'Network' tab and then hit submit (leave all required fields blank).. you shouldn't see anything under that tab because the client validated the form before it was sent to the server
  • [StringLength]
  • [Range]
  • [Compare]
  • [Phone]
  • [EmailAddress]
  • [Url]
  • [RegularExpression]

Furthermore, in order for this to work, in your Bundle.config.. you have a bundle probably called "~/bundles/jqueryval".. and that bundle is not referenced in your _Layout page. But, you probably do have a section at the bottom of your _Layout page called @RenderSection("scripts", required: false)..

So at the bottom of your view that you want to use the validation, you need to write:

@section scripts{
    @Scripts.Render("~/bundles/jqueryval")
}
Grizzly
  • 5,873
  • 8
  • 56
  • 109
1

It's hard to say without being able to actually play with the form, but the client-side validation has to be trigger before the message appears. Submitting the form is of course the brute-force way to trigger validation on all the fields, but otherwise, it's based on the form field state.

The jQuery Validation documentation details:

By default, forms are validated on submit, triggered by the user clicking the submit button or pressing enter when a form input is focused (option onsubmit). In addition, once a field was highlighted as being invalid, it is validated whenever the user types something in the field (option onkeyup). When the user enters something invalid into a valid field, it is also validated when the field loses focus (option onblur). (emphasis mine)

The key is that bit about "once a field was highlighted as being invalid". Until a field has been validated, validation is not run on it automatically unless the value in the field has changed or the full form is validated (such as on submit). If the field is initially empty and you leave it empty, it technically has not changed, and therefore has no validation state. You could potentially add your own trigger to validate the field on blur. For example:

$('#MyRequiredField').on('blur', function () {
    $(this).valid();
});

However, this goes against the convention of the jQuery Validation plugin:

The goal of these interactions is to provide feedback as early as possible, whilst avoiding user annoyance. Displaying error messages before the user had the chance to even type something is not helpful.

Just be aware that there's perfectly good reasons a user might jump around in a form, and it's not always appropriate to display an error message simply because the user hasn't filled out a required field yet.

UPDATE

In my first read through of your question I missed the bit about actually going back to the action before returning the view again, when leaving these fields blank. I seem to remember a similar issue I've encountered before, but I don't remember if it's MVC version-specific or persistent. Basically, if you use TextBoxFor the "required" client-side validation is not applied. Your either need to use EditorFor or apply it manually with @Html.TextBoxFor(m => m.Foo, new { required = true }).

I think you still will have issues with the field not being validated initially as described above, but making that change should ensure that it's actually caught by the client-side validation, rather than going back to the server, first.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444