How does validation in ASP.NET MVC 2 actually work? - asp.net-mvc

I am trying to trace through why my ASP.NET MVC 2 validation isn't working, but I cant find enough about HOW it works to be able to do this.
I have followed the steps in this useful article by David Hayden which seems to be the best documentation currently out there, but nothing actually happens.
I get validation when i submit to the server (as I did since Preview 1 when i added data annotations to my model) but I'm not getting any client side validation.
How can i trace through to test? So far I have verified the following obvious things
MicrosoftMvcJQueryValidation.js and jquery.validate.min.js files are being downloaded
Html.ClientValidationEnabled = true
I cant see easily what is hooking up to which events to know quite how to debug it.

Here's what I've learnt:
MOST IMPORTANT
Your HTML Form must be created with the using directive, not just BeginForm and EndForm.
You must set Html.ClientValidationEnabled = true BEFORE you start your 'Form'
You must use Html.ValidationMessage for each field
You must set Html.ClientValidationEnabled = true on each partial control (ascx)
HOW IT WORKS (very simple overview)
When you do Html.BeginForm it creates a 'FormContext' in the ViewContext
When ValidationMessage helpers are used, metadata is put into the form context
When the form is disposed (by the using statement) it writes out all the validation code
MISC
I cannot seem to get validation working when I have a partial control, if that control uses a different model from the view that defines the Form.
You do NOT need to use Html.TextBoxFor or Html.ValidationMessageFor, you can use Html.TextBox and Html.ValidationMessage

In order for a field to be validated client-side you have to specify a call to Html.ValidationMessage/Html.ValidationMessageFor<> for the field (just like David did in the tutorial you linked) within the view. This is essentially a trigger to the client-side validation logic that you want to run validation for that field.
If there are situations where you don't actually want a validation message to visually appear for each field (i.e. by using Html.ValidationMessage), but would rather allow a summary to be the sole source of validation error messages (i.e. by using Html.ValidationSummary), you still need some way to "trigger" the validation to occur for the specific fields you want it to. This can be achieved by using the Html.Validate/Html.ValidateFor<> methods within your view. Those helpers won't render anything, but will simply register the specified field for client-side validation.
Both of those requirements exist since you might not want the client-side validation to automatically validate every property on your model object, since some of them might not even be part of the form that you're wanting validated.

Related

How does rails handle form validations?

When a rails form fails on the the front end, how is the javascript handled? e.g. a numericality error fails or a required: true option is not fulfilled.
I would like to add a callback function to this. Is there any way to execute some javascript on the failure?
There is no javascript involved on the rendering of the errors on a rails form. The way the errors are displayed are determined by a function called field_error_proc, specifically ActionView::Base.field_error_proc. You can check Ryan Bates' railcast about it
If you are submitting your form via AJAX, you can attach a listener for the ajax:success event. Otherwise the only (ugly) way I can imagine is to do something like:
$(document).on('app:form-has-errors', function() { //do what you have to do} and on the view
<script>$(document).trigger('app:form-has-errors');</script> if the object in the form has errors (#your_object.errors.any?).
I don't think it is possible in another way since the submit and re-render on errors are complete http requests that will clear any events you might bind to the document.
Rails built in validation doesn't operate on the front end (client-side), it is done on the server.
If you want to do javascript validation you will need to find a gem for this or use a jQuery library, or write the validation code yourself.
The normal way form validation is done when using vanilla Rails is like this
The invalid form content is submitted to the controller
The controller uses the content to make the ActiveRecord object
The controller tries to save the object to the Database, kicking off a set of steps that starts with checking the object's validity
The model's validators say it's not valid and "decorate" the object with error messages specifying in which ways it is not valid.
The controller sees that the save failed and renders the view that had the original form in it, providing that view with the object, now with error messages.
The view when it renders the form checks the object for error messages, if it sees any it helpfully displays them to the user. It also populates the fields with the data that the user had previously entered that it gets from the object.

Client validation broken due to NamingContainer ID using ASCX as MVC-View

The situation
I'm currently migrating a WebForms application to MVC. Some old controls make heavy use of the WebForms form validation framework (the old validators, such as RequiredFieldValidator) and for the moment I am dependent on that specific behaviour.
It is far too costly to rewrite those controls entirely. But what I can do, is migrate them into ASCX-MVC-Views. That allows me to use the old validators and controls while using them via Html.Partial etc. in cshtml-files.
The problem
When I do that, I get various JS syntax errors in the generated validation code resulting from bad client IDs of the form controls:
<script type="text/javascript">
//<![CDATA[
var 1_ctl05 = document.all ? document.all["1_ctl05"] : document.getElementById("1_ctl05");
...
The problem here is, that variables may not start with a number (1). That number 1 is where the naming container will usually generate some prefix.
Upon inspection, I noticed that the ID of the ASCX-control is indeed "1", as is the NamingContainer.ID property of the validator and its parents.
Now, I can 'fix' this problem by implementing the PageLoad-Event, and setting this.ID to something starting with a character. But I get the feeling I'm either doing something wrong here (can I set the ID somewhere more properly?) or this is a bug.
Any suggestions?
Though still not entirely sure why the naming container is empty, I noticed that the faulty (and nasty) global JavaScript code can be avoided entirely by using the jQuery unobtrusive validation.
So the "fix" would be using unobtrusive validation whenever working with MVC and ASCX/ASPX forms.

Struts2 and form validation of prefilled forms

I'm using the validate() - method of struts2 to validate the form input. In my struts.xml I can define a result with name "input" which is displayed if the validation fails. This for the context :-)
Now my question: the form I want to validate contains a selectbox which is filled out of a database. The first time the form is displayed everything works fine. But if I validate the form and the "input" - result is displayed, I get an IOException because of the iterator which outputs the db-result into my selectbox. Is there a solution from struts2 or do I have to use a plugin or something like that? Thank you!
When validation fails, it's often necessary to "reload" data for the form page. There's a FAQ entry that covers repopulating controls after validation, mainly detailing the Preparable interface (preferred) and the use of the <s:action> tag (there are some subtle gotchas that can pop up with this, but in general, it's also okay).

ASP NET MVC: Dynamically adding or removing inputs on the form - unobtrusive validation

Before starting, I do have a very particular question and if you want to answer it go straight to the end. But I do welcome comments and advices hence the lengthy post.
OK, we deal with a lot of forms and some of these forms are quite lengthy and have many fields. We also have a requirement - in addition to top level fields - to be able to have variable number of repating rows - as we call them. For example, let's think of a customer which has name, surname and age while it can have zero or many addresses (say 0 to 10) so the user must be able to add or remove contacts from the form while filling it in. So typically user gets and "Add" button to add more addresses and next to each address, a delete button. Potentially there could be more than one repeating section in the same form but I am not going there. The point is, because of legal and historical reasons, all the forms must be saved at once so while the forms can be edited, we cannot accept a half-filled form and have another page for users to add and remove addresses, e.g.
I am using ASP NET MVC 2 (strongly typed views with a single generic controller) with client side validation and heavy jquery scripting for flashy features. We are probably going to migrate to ASP NET MVC 3 very soon and I am already playing with 3 for finding a good solution. These addresses are defined on the Model as List<Address>, e.g.
I currently have a working solution for this issue but I am not satisfied with it: I have an HTML Helper that names the add or delete buttons and a bit of JavaScript to disable validation and allow the form to be posted back (even invalid) and since I can find out the name of the button that was clicked, I have all the necessary logic to handle add or delete and works really well.
But I am posting back and the form is reloaded and I am looking for an aletrnative solution. Here are what I can do:
Do everything in the client side. "Add" button will clone one of such addresses and "Delete" button will remove() the element. I only have to rename the indexes which I have done. We were using jquery calendar and it was breaking on the new elements which I have also fixed. But the validation is not working which can probably work with ASP NET MVC but this solution looks like a very brittle one - a house of card which looks great before you add another card.
Post the whole page usin Ajax and then load it back again: This is probably better than my current solution but only slightly.
Use ajax to post the form and get back JSON and use the data to build the elements or remove them: Again a house of card because of extensive client side scripting
Serialize the form and post using Ajax to a particular action and get back only the repating section (as a partial view). The action on the controller can be reused and called from the view itself to return the partial view
OK last one is the one I am working on but there is an issue. ASP NET MVC 3 with unobtrusive validation works only if the form is engulfed in a BeginForm() while my top level view has a BeginForm() but not my partial view. It works well when I call it from the view but not on the ajax call to get just the repeating section.
(Question)
So is there a way to tell ASP NET MVC 3 to spit out validation data atttributes regardless being in a BeginForm() block?? To be honest if this is not a bug, this is definitely an important feature request. I have in fact used reflector to disassemble the code and the condition seems to be there.
Short Answer:
Add this to the partial view:
if (ViewContext.FormContext == null)
{
ViewContext.FormContext = new FormContext();
}
I don't think it is possible using the default unobtrusive libraries supplied. If you look at jquery.validate.js and jquery.validate.unobtrusive.js it looks like it only validates what is inside the form.
There's a few posts about it if Googled and a few work arounds.
I had a similar issue (although much simpler) where I had a validation summary at the top of the page and multiple forms but the unobtrusive javascript would only populate the view summary if its inside the form (jquery.validate.unobtrusive.js line 39 if interested...).
I'm not sure if the validation library is extendible but most things in jquery are so that might be an option if you want to go down that road.
As far a possible solution to your problem I'll put in my 2 cents for whats its worth.
You could have two actions that are posted to. The first action is you post your model with no js validation and all validation is handled in the code - this will catch all user with javascript turned off.
Your second action is you serialized the model. In mvc 3 using the Ajax.BeginForm has an AjaxOption for Url where you can specify an action for the jquery to call (where it serializes the form form you and you can decorate your action with your strongly typed model). Here you can check the model and return a json result and handle this in the javascript.

How to do in-line form validation of multi-field business rules (repository) in MVC?

There is a study here my co-worker took to my notice. Basically, that in-line form validation is a good thing.
But how would you do in-line multi-field form validation in MVC assuming you already have a "yield return" setup to return a list of form violations? Is the in-line validation only for primitive values like "a zip code should not include alpha characters?"
Would you submit some Javascript code to the client that checks that "this field and this field should be evaluated together firing this validation, and oh by the way, we are going to validate all fields again on a final submit?
Anyone have code example (C# and MVC) to illustrate handling in-line multi-field form validation using a remote repository (but not all fields at one time)?
I don't have any code but if I was going to do inline validation I would implement the validation sample in Nerd Dinner.
Clearly this would validate on a submit so not really useful to your question. However, if you couple it with jQuery then it does become useful.
Essentially I'd be doing a jQuery postback at key points, checking for validation errors, and then highlighting the errors to the user in the callback.
You can attach events to say the lost focus events of fields that have a certains style class or to all fields etc. Really quite extensible in that regard.
There are tons or samples on jQuery and how to post back etc.
I'd also still be doing the full validation check on postback as well just to catch anything that may have been missed.
Does this help?

Resources