Using Knockoutjs And Server Side Validation in .NET MVC2 - c#

I'm using MVC2.
Whats the recommended way of server side validation of forms when using knockout?
Currently, most of my forms are in partial views, which have a C# ViewModel with Validation Attributes. Something like this:
public class SomeThingViewModel
{
[Required]
public string Name { get; set; }
[Required]
public int Number{ get; set; }
}
So when the form gets submitted to the server, I get all the model errors and I can return the form with errors, which are displayed with something like: <%: Html.ValidationMessageFor(m => m.Name)%>. This is then reloaded into the element that holds the form on the main page so that the user can see the errors. This would kill any bindings I had with the form in knockout I would assume.
I'm not really sure how to go about this using knockout.

This can be tricky, but done right works like a breeze.
First, synchronize your viewmodels. What you have client-side in knockout you pass exactly to the server. Second, don't do server-side HTML with knockout. Create fields that are set server-side and read client-side that indicate the validity of each data field in your ViewModel.
So if your Model has a field Name, your ViewModel has Name and Name_ValidationResult, which is an enum that indicates whether or not the Name field is valid and why it's not. If your server-side validation fails, set your validation result fields and pass the whole server-side ViewModel back to the client to be re-set as the client-side ViewModel after the request completes. Basically, you a re-creating the ViewState portion of ASP.NET, but doing so in a format that will work with Knockout.js
On the client-side, you have error messages that only show based on values of the ValidationResult fields. So you might have a canned error message that states "The Name field must be set" that is only displayed if Name_ValidationResult has the value "Empty" (for example).
Basically, you actually use the MVVM pattern with a minor tweak to account for having to round-trip to the server.
So you are suggesting that I add ValidationResult fields in my C# ViewModel for each property. Then set the ValidationResult Properties in my controller when I check for the Model's validity. Then pass back the viewmodel as JSON? so that I can update my knockout viewmodel. This will require me to manually validate to some extent right? Or can I leverage the ModelState errors that I will end up with? – Blankasaurus
Bottom line is yes to all your questions.
In truth, I missed the fact that you were using DataAnnotations for your validation, or I'd have mentioned it. You should be able to leverage ModelState errors to set your validation results that you pass back to your knockout page.
The problem is that you're using two fundamentally incompatible technologies and hoping they'll play nice together, and I don't think that's going to work out the way you hope. Something is going to have to give, and I suggest that the best point for that is server-side. Drink the knockout cool-aid and fix what you have to server-side.

Related

Which part of the code shows validation errors after Post in asp.net MVC?

Let's say I have one view model. It has one required Name property. And I have disabled client-side validation. I have this code in my action method:
if (!ModelState.IsValid)
{
return View(model);
}
So, everything works fine. It will highlight the required field after post. But, I can't understand that which jQuery validaion function do this process? How, jQuery Validation detects that the form has been submitted once?
I want to find that code, because I want to change it slightly. For example, I have my own helpers, which has custom validation logic. For now, my custom helper validation are not showing after invalid Post. And, I want to add my logic to the built-in function, which I CAN NOT FIND ANYWHERE.
Firstly, if you have disabled client side validation, jquery validation has nothing to do with it (you have disabled it!). To briefly explain what happens when you post and return the view.
The DefaultModelBinder initializes a new instance of you model
The DefaultModelBinder then reads the form data (name/value pairs)
and if a property name matches one of the form data values, its
property is set (assuming its valid) and its value is also added to
ModelState. If the value is not valid, the property is not set but
its value is added to ModelState (the attemptedValue) along with
a ModelState error
When you return the view, your #Html.ValidationMessageFor() method
reads the ModelState values and if there is an error associated
with the property, the error message is added to the html generated
by the ValidationMessageFor() method and the relevant class name
(which highlights it) is added
You can inspect the source code for the DefaultModelBinder and ValidationExtensions if you want to see more detail of how these work.
As for "I want to find that code, because I want to change it slightly", then DONT. You have not indicated what you trying to do, or shown any code for your html helper extension method, but html helpers do not (and should not) contain validation logic. They are responsible for generating html based on a property and the validation attributes applied to that property.
If you have custom validation logic for a property, then you create an attribute that inherits from ValidationAttribute (and if you also want client side validation then it also needs to implement IClientValidatable). A good guide for creating your own validation attributes is this article.
Mvc has its own validation that is not server side and works with the data annotations you set on your model. On post it simply goes to the controller then checks the modelstate errors if its valid it runs your code in the function, if not it returns the model with its errors. If you had jquery validation it would never go to the controller in the first place. When server side validation is enabled the validation is done before the form is sent to the controller. Without jquery it is validated at the controller. If not what you're looking for please let me know

Access HtmlHelper right after it is created, and before the view

I know that i from Application_Start can ActionFilterAttribute add a custom global filter and manipulate the ModelState and what not.
Is there a similar way, to access the #Html (HtmlHelper) Before it get's send to the view?
The reason for this is that i want to edit (or remove and recreate) the UnobtrosiveValidationAttributes. And if i try to do that in the View like this: #Html.GetUnobtrusiveValidationAttributes("PhoneNumber").Clear(); Nothing happens, but i'm thinking it might would work if i got to it earlier?
(If you are wondering why: i need to translate the ErrorMessages inside)
I'm not sure if there are ways to intercept where the unobtrusive validating code is assigning the message text. I'm not sure that it's the best idea either because one property could have many different validations (Required, Regex, StringLength, etc...)
I can tell you there are other ways to localize error messages though. One way that works out of the box is to use resource files and to define a resource key instead of an error message.
[Required(ErrorMessageResourceName="resource-key")]
public string PhoneNumber { get; set; }
Another way that works but requires writing more code is to create your own custom validators that retrieve your error messages from wherever they are stored. I had to recently do this because all of our localization happens in the database.

How to add MetaData to a dynamically build MVC3 ViewModel?

One of the key features of a project I'm working on is the ability for the user to configure Forms (as in "Forms" to fill-up) based on a pool of pre-existing field types (well known types, for instance "user name", "date of birth" etc. but also "generic types" like "string", "DateTime" etc.).
We used to have a static ViewModel that worked fine for the "well known" types and looked like this:
public class UserInputModel
{
[StringLength(200)]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
public DateTime BirthDate { get; set; }
//Here comes a lot of other properties
}
All the known properties were listed and we were showing or hiding them given the context.
But the last requirement came and changed all that. The user shall now be able to add as many generic type fields as he wants. In order to do this, we decided to make this InputModel entirely dynamic. It now looks like this:
public class UserInputModel
{
// Each ModelProperty has an "Id" and a "Value" property
public ICollection<ModelProperty> Properties { get; set; }
}
This works like a charm. The razor view only has to iterates over the collection, create the corresponding controls for each property of the collection in a more than standard way:
#Html.TextBoxFor(m => m.Properties[index].Value);
... and we nicely get the data back as a filled form.
=> This works fine, but we don't have any client-side validation. For this, we would need some Metadata... which we don't have via annotations anymore since we're dynamically creating the model.
In order to provide those MetaData, I created a CustomModelMetadataProvider that inherits from DataAnnotationsModelMetadataProvider and registered it as the new ModelMetadataProvider in the Global.asax. The CreateMetadata() function gets called upon creation of the ViewModel, and that for each of the properties of my ViewModel... sofar so good.
Where the problem starts: in order to add some metadata to the current property, I first need to identify which property I am currently looking at ("Name" has a maxlength of 200, "date of birth" hasn't so I cannot assign a maxlength to every property per default). And somewhow I didn't manage to do that yet since all the properties have the same name Value and the same container type ModelProperty.
I tried accessing the container of the property via reflection, but since the ModelAccessor's target is the ViewModel itself (because of the lambda expression m => m.Properties), the following construct gives me the ViewModel as a whole, not just the ModelProperty:
var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);
I've been flipping this over and over but cannot find a way to identify which ModelProperty I have in hand. Is there a way to do this?
Update: after flipping this in every possible direction for a while, we finally went another way. We are basically using unobstrusive javascript to use MVC's validation capabilities without touching attributes nor metadata. In short, we add HTML attributes like value-data="true" (and all other required attributes) to the #Html.TextBoxFor() statements. This works wonderfully for all the atomic validations (required, stringlength etc.).
Tim, you can leverage what appears to be client-side validation through Ajax with the Remote attribute on your properties.
Basically, you'll need to set up a validation controller and then write some smarts into that controller. But at least you'd be able to write some helper methods and keep it all in one place. You would have a series of validators, based on the meta data that you are presenting to the end users, and each validator method would work for a particular type with good re-use.
The one pitfall to this approach would be that you would need to write a validation method for each type and condition that you want to support. Sounds like you're having to go down that road anyways, though.
Hope this helps.
See if this article help you: Technique for carrying metadata to View Models with AutoMapper.
Also use this one for ideas (custom model metadata provider): changing viewmodel's MetadataType attribute at runtime
Fluent validation is probably the best option for you in my mind, but its obviously up to you to select the best match among those above.
Update
Try use ModelMetadata and override ModelMetadataProvider: Dive Deep Into MVC: ModelMetadata and ModelMetadataProvider. This way you completely customize your model metadata (this replaces data annotations) and you have complete control on what is happening, rather than relying on ASP.NET MVC.
Another good place to look at it is Creating your own ModelMetadataProvider to handle custom attributes.
Hope this all is of help to you.

Validation in ASP.MVC 3.0

I have a model class :
public class YearlyChageRate
{
public int Year { get; set; }
public double Rate { get; set; }
}
and I want to check that Yeae is unique or no and in condition Year is not unique application show an error message to users.How can I check the Year filed is repeated or not?
Here is a good example:
http://tugberkugurlu.com/archive/asp-net-mvc-remote-validation-for-multiple-fields-with-additionalfields-property
And here too: MVC validation for unique
You can use Remote attribute in your model to perform check for unique value in database.
This is official example of Remote attribute: http://msdn.microsoft.com/en-us/library/gg508808(v=vs.98).aspx
And one more: http://www.a2zdotnet.com/View.aspx?Id=198
You could use the [Remote] validation attribute on your view model.
Although you can use DataAnnotations attributes for validation and the [Remote] attribute for checks against the DB, it's not a very good design choice.
Let me explain:
data access is a data-layer matter
validation is a business-layer matter
user input and feedback is a ui matter
With DataAnnotations, you're mixin 3 in 1. It can be faster, but surely not well designed.
You could try a more disciplinate approach, like this:
Have a method at business level that will take your object as a parameter, perform validation internally using a validation framework of your choiche;
This method will call the data access to persist the object only if the validation passed;
This method will always return to the UI the validated object, plus a collection of fields/errors if anything didn't validate;
When you read the output of the method in your ui, you can either display a success page if there were no errors, or redisplay the form with the validation errors returned. To do this, the use of the PRG pattern is highly recommended, as you should never display a page on a POST method. Google for the PRG pattern to learn more about it. MvcContrib has a nice ActionFilter called ModelStateToTempData to make the implementation of the PRG pattern something trivial.

ASP.Net MVC Error Validation - How to display validation message when passing a custom view model to a view

I have been adding error and business validation to my app, and when I test using a view that was strongly typed to one model, let's say locations, I get the validation summary as well as the validation messages for each field that didn't pass, plus my css highlights the appropriate field as expected...
...when I try this with a view that's strongly typed to a custom view model, let's say I passed it location - so the user can enter a new one, as well as
IEnumerable<Location> locations
, so it will list out all of the existing locations below the new location form. When I do this I get the validation summary, but the message for each field is not displaying, nor is the css applying the * and the highlighting to each incorrect field.
I am using Linq to SQL, so I have added all of my validation as partial classes, so in this case all of the validation comes from the partial class location, which to my understanding will compile with the designer file that linq to sql created and add my business validation. Is this problem happening because I am not passing the view MyApp.Models.Location, and instead passing it MyApp.Models.MyCustomViewModel? if so, what's my best approach so that I can use just one form for create and list?
I was able to get this working - I changed the return value in my POST method to return
return View(new MyCustomViewModel(location) { });
instead of
return RedirectToAction(new MyCustomViewModel(location) { });
hope this helps if anyone stumbles across the same problem I did - which was my own misunderstanding of how View works differently than RedirectToAction.

Categories