Remote ViewModel validation of nested objects not working - asp.net-mvc

I have a class user which looks like this:
public class User
{
public int UserId { get; set; }
[Required(ErrorMessage = "A username is required.")]
[StringLength(20, ErrorMessage = "Your username must be 4-20 characters.", MinimumLength = 4)]
[RegularExpression("^[a-zA-Z0-9]*$", ErrorMessage = "Your username can only consist of letters and numbers.")]
[Remote("UsernameExists", "RemoteValidation", ErrorMessage = "Username is already taken")]
public string Username { get; set; }
[Required(ErrorMessage = "A password is required.")]
[MinLength(4, ErrorMessage = "Your password must have at least 4 letters.")]
public string Password { get; set; }
[Required(ErrorMessage = "An email address is required.")]
public string Email { get; set; }
}
For the Register functionality I have created a ViewModel that holds a User object and a string for the password confirmation:
public class RegistrationViewModel
{
public User User { get; set; }
[DisplayName("Password confirmation")]
[Required, Compare("User.Password", ErrorMessage = "The password do not match")]
public string PasswordConfirmation { get; set; }
}
The first problem I run into is that I can't seem to get the validation for Compare("User.Password") to work as it does not seem to find the property on the user. Is there any way to validate the PasswordConfirmation property against the User.Password property?
The second problem is the Remote validation of the Username field. I followed David Hayden's tutorial at http://davidhayden.com/blog/dave/archive/2011/01/04/ASPNETMVC3RemoteValidationTutorial.aspx but the parameter username in the UsernameExists method is always null. Am I missing something here?
Edit:
I'm sorry but I was actually not clear enough on the error I receive for the password comparison. It works fine when filling in the fields, if the passwords do not match I will receive an error. However, when submitting the form I get the following error in the validation summary: Could not find a property named UserToRegister.Password.
Edit 2:
I have figured out part of the problem thanks to Joe's post. The remote validator posts back URL/?UserToRegister.Username=temp which obviously does not match the username parameter of my controller action. In order to map my action parameter to UserToRegister.Username the following is required:
public ActionResult UsernameExists([Bind(Prefix = "UserToRegister.Username")]string username)
This now correctly passes the parameter to the method. However I still get the error when using the Compare attribute on the password field.
Thanks.

The issue with the validation of the PasswordConfigurmation property against the User.Password property is caused by a bug in in the 'jquery.validate.unobtrusive.js' file.
Originally, the jquery 'equalTo' function is:
adapters.add("equalto", ["other"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
setValidationValues(options, "equalTo", element);
});
You just need to modify this line:
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
to:
element = $(options.form).find(":input[name='" + fullOtherName + "']")[0];
Note the addition on the single quotes around the 'fullOtherName' selector. Once you've made this change, the client side validation works as expected.

For the remote validation part nothing jumps out at me. It might be helpful to open up firebug and see what the request that is being fired looks like. You should see something roughly like this if everything is properly wired up...
http://localhost:14547/[Controller]/[ActionName]?[ParamName]=[paramValue]
From the request you provided, you can either use a prefix like you ended up doing or you can make your action take a user named UserToRegister and then within the action access the UserName property. This is the recommended way of dealing with remote validation of objects and might be a little easier to think about than using a Bind attribute.
For the compare validation, it appears that on the client side validation is succeeding but on the server side validation fails because the validation context does not contain a property named User.Password, only a property named User.

Inheritance seems like a standard way of adding functionality in this case. How about having your RegistrationViewModel derive from the UserViewModel:
public class RegistrationViewModel : UserViewModel
{
[DisplayName("Password confirmation")]
[Required]
[Compare("Password", ErrorMessage = "The password do not match")]
public string PasswordConfirmation { get; set; }
}
and:
public ActionResult UsernameExists(string Username)
{
...
}

Related

How to put validation for a model created by Entity Framework?

I have created a database, added Entity Framework reverse engineering (in a Dll). I have created edit view, delete methods, everything is working fine.
I need to add validation. How to do that?
using System;
using System.Collections.Generic;
namespace MyDLL.Models
{
public partial class ContactNumberTable
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
}
This C# file is automatically created inside my dll project references model.
Just extending the jamiedanq answer, If you only need to make the field as "REQUIRED" then put the
[Required(ErrorMessage="your message")]
on top of your properties.
If you need to validate some field from database (you would need to call a validation method on server from your view) then simply put,
[Remote("action name", "controller name", HttpMethod = "POST")]
on top of your fields.
It will call the action method by an AJAX call. You would need to add Jquery Validation in your views.
You need to decorate your properties with validation attribute.You can have muliple validation on single propery as shown below:-
[Required(ErrorMessage = "Please enter Mobile")]
[RegularExpression(#"^([7-9][0-9]*)$", ErrorMessage = "Should be numbers only starting with 7,8 or 9")]
[StringLength(10, ErrorMessage = "Mobile should be of Max Length 10 Characters only")]
public string Mobile{ get; set; }
EDIT
First to start with this is what is called Data Annotations.To add validations to your Model you can add Required[(ErrorMessage="")] which is a DataAnnotation Class on any value you want to validate like below
[Required(ErrorMessage = "Validation message here")]//this is what sets the validation message
public string Name { get; set; }
Then in your View you have to do the below to get the Validation message showing when required:
#Html.TextBoxFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name, new { #class = "text-danger" })//this is the part the gets the validation error message
What this does is that it gets the error message for the value you put the Validation message on in your View. You can do this for any value you want Validated.
EDIT
There are several Data Annotation classes that you can use, you can read more here Link and here Entity Framework DataAnnotations

DataAnnotations localization message issue for RegEx validation

I have a problem with the validation message for Regular expression. Localised messages appear everywhere except for one field below:
[LocalizedDisplayName("LblWordCount", NameResourceType = typeof(ValidationMessages.Messages))]
[Required(ErrorMessageResourceName = "ErrorFieldRequired", ErrorMessageResourceType = typeof(ValidationMessages.Messages))]
[RegularExpression(#"^[0-9]+$", ErrorMessage = "", ErrorMessageResourceName = "ErrorDigitsOnly", ErrorMessageResourceType = typeof(ValidationMessages.Messages))]
public Int32 WordCount { get; set; }
It doesn't matter what I put in the resx file for "ErrorDigitsOnly" - the default message always kicks in: "The value 'zxzza1' is not valid for Word Count." For instance - message for the [Required] appears correctly.
Any suggestions for that?
Cheers,
303
I have checked the code for spelling mistakes but couldn't find any.
I actually ran across a similar situation myself.
Have you tried setting the wordcount validation in another class instead of using the regex pattern in the DataAnnotation Attribute?
For example -
public class EmailAttribute: RegularExpressionAttribute
{
public EmailAttribute() : base(#"[^#\.]+(\.[^#\.]+)*#[^#\.]+(\.[^#\.]+)?(\.[^#\.]{2,})")
{
}
public override string FormatErrorMessage(string name)
{
return Resources.Resources.emailValidation;
}
}
You can then use the attribute like so -
[CustomRequired]
[Email]
public string Email { get; set; }
The advantages of using it this way means that you get strong typing on your resources and it also allows you to write neater validation classes.
Hope it helps!

How to clear textboxes defined with MVC HTML helpers

I can't figure out how to do this very simple thing: My page contains a set of textboxes that a user can fill out to add an item to a list. Then the item shows up in a dropdown list.
At that point, I want the "add" textboxes to be cleared. This is the behavior expected by most users, I think. The item has been added; now the textboxes should be empty, ready for the next item to be entered.
However, I can't seem to clear them when I am using Html helpers, e.g., Html.Textbox(...). I like these controls because of the way they "remember" the input in case of input error. However, unlike webforms controls, you can't set them programmatically. They continue to retain the values until the user enters something else.
Is there any way around this behavior? I thought of clearing them in javascript, but I don't want to do that if there are any errors.
UPDATE some of the code;
One of my textboxes in the view:
<h6 style="margin-top: 0px">Add custom email template:</h6>
<div style="margin-top: 10px">
<div class="label">Name:</div>
<%= Html.TextBox("addName", "", new { #class="formtext", style="width: 400px" }) %>
<div class="alerttext"><%= Html.ValidationMessage("addName") %></div>
</div>
The class I am using for model binding:
public class ManageEmailTemplatesSubmittedData
{
[RegularExpression(RegExpressions.templateNameRestrict, ErrorMessage="Names should begin with a character and consist of only characters and numbers")]
public string addName { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addDescription { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addSubject { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string addTemplate { get; set; }
public string templates { get; set; }
[RegularExpression(RegExpressions.templateNameRestrict, ErrorMessage = "Names should begin with a character and consist of only characters and numbers")]
public string editName { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage="Invalid entry; please omit unusual characters")]
public string editDescription { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string editSubject { get; set; }
[RegularExpression(RegExpressions.freeTextRestrict, ErrorMessage = "Invalid entry; please omit unusual characters")]
public string editTemplate { get; set; }
}
My action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CustomEmails(SubmitButtons buttons, ManageEmailTemplatesSubmittedData data)
{
bool saved = false;
string selectedTemplate = data.templates;
if (ModelState.IsValid)
{
ButtonStyles buttonStyles = ButtonStylesCreator.GetSelectListButtonStyles(rc.persistedData.loggedInUser.userType);
Notification notification = new Notification(rc);
if (buttons.addTemplate == buttonStyles.addEmailTemplateButtonValue)
{
// add an email template
notification.SaveCustomTemplate(data.addName, data.addName, data.addTemplate, data.addSubject, data.addDescription);
saved = true;
}
else if (buttons.saveTemplate == buttonStyles.saveTemplateValue)
{
// update an email template
notification.SaveCustomTemplate(data.templates, data.editName, data.editTemplate, data.editSubject, data.editDescription);
selectedTemplate = "";
saved = true;
}
}
ConfigureEmailsModelBuilder builder = new ConfigureEmailsModelBuilder(rc, rc.persistedData.loggedInUser.userID, selectedTemplate, true, saved);
return View(builder.Build());
}
ConfigureEmailsModelBuilder constructs the view model, which includes a SelectList that is the dropdown list of the items that have been added. (The view is strongly typed to the type generated by builder.Build).
The HTMLHelper's first look at the ModelState and ViewData to see if any values match their key and then finally use whatever value you provide them.
If you need to reset the textboxe's value you also need to clear the ModelState entry with the matching key. Another alternative is redirecting to the same page instead of simply rendering a view via javascript or with MVC.
This is working for me on an MVC3 site log on page.
ModelState.Clear();
model.UserName = string.Empty;
model.Password = string.Empty;
ModelState.AddModelError("", "The user name or password provided is incorrect.");
This will clear the login textboxes used for password and username, and keep any model errors.

How do I extend ASP.NET MVC2 out-of-box validation to validate creditcard / emails?

I've been looking at the file MicrosoftMvcJQueryValidation.js which is the layer between your page and the jquery.validate object in ASP.NET MVC 2 Beta.
It will allow any type of validation rule supported by jquery.validate and had additional special handling for regularexpressions, strings, ranges and required fields. If it is a generic/unknown rule type it will just pass through the parameters like this :
default:
__MVC_ApplyValidator_Unknown(rulesObj,
thisRule.ValidationType, thisRule.ValidationParameters);
break;
However - I cannot seem to figure out how to inject additional rules into the JSON that is generated by the framework, such as 'email'. Normally the rules just come from the attributes such as [Required].
I know there are lots of extensivbility points to replace the whole validation metadata provider - but I'm looking for a simple way.
How can I use - for instance the 'email' or 'creditcard' validators in conjunction with a simple model like this:
public class LoginDetails
{
public bool Editable { get; set; }
[Required(ErrorMessage="Please enter your email")]
public string Username { get; set; }
[Required(ErrorMessage="Please enter your password")]
public string Password { get; set; }
}
For different types of data you can keep the Required message but also add different attributes below them. For Email and Credit Car a Regular Expression like this would probably work best.
[Required(ErrorMessage="Please enter your email")]
[RegularExpression(#"\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b",
ErrorMessage="Please enter a valid email address.")]
public string Email { get; set; }
[Required(ErrorMessage="Please enter your password")]
[RegularExpression(#"^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$.",
ErrorMessage="Please enter a valid Credit Card.")]
public string CreditCard { get; set; }
Check out this, this and this link for more info.
Use a DataAnnotationsModelValidator
Like this.
Use these includes
/Scripts/jquery-1.3.2.js
/Scripts/jquery.validate.js
/Scripts/MicrosoftMvcJQueryValidation.js
Use a validation function
<script type="text/javascript">
$.validator.addMethod("price", function(value, element, paras) {
if (value.length == 0) {
return true;
}
if (value > paras.minValue) {
var cents = value - Math.floor(value);
if (cents >= 0.99 && cents < 0.995) {
return true;
}
}
return false;
});
</script>

ASP.NET MVC: DataAnnotations - Show an error message indicating that a field must be numeric

There appears to be something of a hole in the way DataAnnotations works in that a user entering in some text into a field that will go into an int will never reach the DataAnnotations code. It kicks off a model binding error and displays the error to the user "The value 'a' is not valid for the XXXX field."
Anyway, it's all very nice that it automatically handles this situation, but I actually want to display an error message indicating the problem eg. "The value 'a' is not numeric. Please enter in a numeric value for the XXXX field".
I have tried the solutions set out How to replace the default ModelState error message in Asp.net MVC 2? and ASP.NET MVC - Custom validation message for value types, but I can't get them to work.
It appears that my resource file is not being read at all, since here (http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder.resourceclasskey.aspx) it states "If the property is set to an invalid class key (such as a resource file that does not exist), MVC throws an exception." and even if I change the line to DefaultModelBinder.ResourceClassKey = "asdfasdhfk" there is no exception.
Anyone have any ideas?
EDIT: Here is some code. All of it is working minus my Messages.resx file's messages are not being used. The code for Messages.resx is auto generated so I won't include it.
So entering "a" into ProcessOrder results in a generic message rather than what I have entered into Messages.resx for PropertyValueInvalid (and InvalidPropertyValue for good measure).
Application_Start method
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder(); //set dataanooations to be used
DefaultModelBinder.ResourceClassKey = "Messages"; //set data annotations to look in messages.resx for the default messages
ValidationExtensions.ResourceClassKey = "Messages";
}
Entity Class
[MetadataType(typeof(GLMetaData))]
public partial class GL
{
}
public class GLMetaData
{
public int TransRefId { get; set; }
[DisplayName("Process Order")]
public int? ProcessOrder { get; set; }
[DisplayName("Trans Type")]
[StringLength(50)]
public string TransType { get; set; }
[StringLength(100)]
public string Description { get; set; }
[DisplayName("GL Code")]
[StringLength(20)]
public string GLCode { get; set; }
[DisplayName("Agents Credit No")]
[StringLength(50)]
public string AgentsCreditNo { get; set; }
[Required]
public bool Active { get; set; }
}
Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(GL glToBeUpdated)
{
try
{
if (!ModelState.IsValid)
return View(glToBeUpdated);
//set auto properties
glToBeUpdated.UpdateDate = DateTime.Now;
glToBeUpdated.UpdateUser = this.CurrentUser;
glDataLayer.update(glToBeUpdated);
glDataLayer.submitChanges();
return RedirectToAction("Index");
}
catch
{
glDataLayer.abortChanges();
throw;
}
}
What I did to combat a similar issue was to clear the model state, validate against ModelState["XXXX"].Value.AttemptedValue instead of against the nulled value caused by an trying to put an invalid value into the Model's property, populating the error messages and resetting the Model values.
That way I can have the error messages I want and if necessary offer more than one ("a value is required" or "the value must be numeric").
I have battled this for most of the day on MVC4 RC. No matter what i set
DefaultModelBinder.ResourceClassKey
to it never seemed to work. It also never threw an exception when I assigned junk.
This is what I was using to assign the value (to no avail):
DefaultModelBinder.ResourceClassKey = typeof(App_GlobalResources.ValidationMessages).Name;
In the end I decided to tackle this error message on the client side and override the data attribute that jQuery uses to display the message.
#Html.TextBoxFor(m => m.Amount, new Dictionary<string,object>(){{"data-val-number","Invalid Number"}})
this is working how I need it to.
Ironically this works too:
#Html.TextBoxFor(m => m.Amount, new Dictionary<string, object>() {{ "data-val-number", HttpContext.GetGlobalResourceObject("ValidationMessages", "PropertyValueInvalid") } })
Here I have taken Contact number field as string but with Range Attribute so can provide numeric validatioin to use if from your Resource file .
[Required(ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ContactNumberRequired")]
[Range(0, int.MaxValue, ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ValidContactNumber")]
[Display(Name = "Contact Number")]
public string ContactNumber { get; set; }
So now here provided ErrorMessageResourceName as key . You can customize and use it also in Multi Language

Resources