Hello, I am trying to make some sense of this validation stuff in MVC2. I followed various walkthroughs, all them for betas/rcs... and I cant' get anythign to happen. Note: THIS JQUERY - NOT THE MS AJAX STUFF!
I have referenced MicrosoftMvcJQueryValidation.js which i got got the mvc2 futures lib.
My model is:
public class User
{
protected User()
{
Roles = new List<Role>();
}
public int UserId { get; set; }
[Required(ErrorMessage = "")]
[DataType(DataType.Text)]
public string Username { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[DisplayName("First name")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "")]
public string FirstName { get; set; }
[DataType(DataType.Text)]
[Required(ErrorMessage = "")]
public string Surname { get; set; }
[Required(ErrorMessage = "")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DisplayName("Disabled")]
public bool CanLogin { get; set; }
public DateTime? LastLogin { get; set; }
public IList<Role> Roles { get; private set; }
public string Name
{
get { return string.Format("{0} {1}", FirstName, Surname); }
}
public bool HasPermission(string permission)
{
foreach (Role r in Roles)
{
if (r.Permissions.Count(p => p.Name == permission) > 0)
return true;
}
return false;
}
}
Which is interesting/annoying for a few reasons. Firstly all of my annotations appear to have been ignored.
Secondly FormId isn't even marked as required, and as this rendered text was taken from a page loaded with a null Model (add mode) the FormId field isn't in the form. Finally, this doesn't even result in any actual validation occuring (on the
client side). All the server side validation is working fine.
Are there other methods I need to call...?
PS,
See I am using PasswordFor instead of EditorFor. Because someone at MS testing dropped the ball there. Yes EditorFor renders a password field, but it behaves differently to PasswordFor.. the value of the Password field is sent to the client (set in the value
attribute of the field). Fail.
-- Sam Critchley
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
The former shows the error message next to the field; the latter doesn't show any message at all (just marks the property as something to be validated, which means you should probably have a call to Html.ValidationSummary() somewhere to show all the error
messages).
Marked as answer by ricka6 on Apr 09, 2010 09:35 PM
Also, your [Required(ErrorMessage = "")] isn't legal. Either remove the set of ErrorMessage to get the default message, or set an actual message in the setter.
Thanks for getting back to me. I figured out I needed a ValidateFor when i started rummaging around MVC2 source code looking for where meta data was populated from. Seems reasonable, however the "" ErrorMessage thing isn't! Not only should this be allowed
but having it there causes all the other attributes to silently fail. For example if i have ErrorMessage (with "") and DisplayName on a property neither of the attributes manifest themselves.
Jquery validate is very extensible and I have a pretty non default customisation of the error display element, I personally only show error messages for anything other than required field errors. Required field errors are indicated by highlighting the field
in question. You are basically telling me I can't do that because MS couldn't envisage a scenario where that would be wanted. You envisioned wrong!
I will now probably end up setting the errormessage to something like '--' and strip it out on the client...
-- Sam Critchley
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
Heres my validate customisation. Css not included:
var validatorDefaults =
{
errorPlacement: function(error, element) {
var errorContainer = this.errorContainer.split(',')[0];
if (error.html().length > 0 && error.html() != '--') {
var li = $('<li></li>');
li.appendTo(errorContainer);
error.appendTo(li);
li.attr('for', element.attr('id'));
}
},
highlight: function(element, errorClass) {
if (element.tagName.toUpperCase() == "INPUT" && element.type.toUpperCase() == "RADIO") {
var name = element.name;
var elements = $('input[name=' + name + ']');
elements.each(function(index, item) {
$(element.form).find(".inputlist label[for=" + item.id + "]").addClass('highlightlabel');
});
}
else
$(element).addClass(errorClass);
},
unhighlight: function(element, errorClass) {
if (element.tagName.toUpperCase() == "INPUT" && element.type.toUpperCase() == "RADIO") {
var name = element.name;
var elements = $('input[name=' + name + ']');
elements.each(function(index, item) {
$(element.form).find(".inputlist label[for=" + item.id + "]").removeClass('highlightlabel');
});
}
else
$(element).removeClass(errorClass);
},
errorContainer: "#messagebox, #errorHeading",
errorClass: "input-validation-error"
}
I render this above my form:
<h6 id="errorHeading">
There are errors in the information entered</h6>
<ul id="messagebox">
<% foreach (var error in ViewData.ModelState.Where(p => p.Value.Errors.Count > 0))
{
foreach (var x in error.Value.Errors.Where(p => p.ErrorMessage.Trim().Length > 0))
{
%>
<li>
<label class="input-validation-error" for="<%= error.Key.Replace(".", "_").Replace("[", "_").Replace("]", "_") %>">
<%= x.ErrorMessage %></label></li>
<% }
} %>
</ul>
I'm halfway through implementing this validation stuff so maybe my foreach loop can go... see how the validation thing works when/if i get it working.
Also shouldn't DataType(DataType.Email) trigger a email : true rule? Likewise for URL..
Edit: after looking at MicrosoftMvcJQueryValidation.js I have to say I am deeply unimpressed. Its completely non-extensible (other than just overriding existing methods) and we're forced to use your own customisation of the display. If i want to add additional
rules via the client i have to use the .rules method for each field... etc etc.. not good, not using it.
-- Sam Critchley
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
Thanks for getting back to me. I figured out I needed a ValidateFor when i started rummaging around MVC2 source code looking for where meta data was populated from. Seems reasonable, however the "" ErrorMessage thing isn't! Not only should this be allowed
but having it there causes all the other attributes to silently fail. For example if i have ErrorMessage (with "") and DisplayName on a property neither of the attributes manifest themselves.
Jquery validate is very extensible and I have a pretty non default customisation of the error display element, I personally only show error messages for anything other than required field errors. Required field errors are indicated by highlighting the field
in question. You are basically telling me I can't do that because MS couldn't envisage a scenario where that would be wanted. You envisioned wrong!
I will now probably end up setting the errormessage to something like '--' and strip it out on the client...
It sounds like what you want is outside the architecture of our system. It's hard to follow exactly what you're trying to do, but you should consider providing a full example as a feature request.
Marked as answer by ricka6 on Apr 09, 2010 09:34 PM
Edit: after looking at MicrosoftMvcJQueryValidation.js I have to say I am deeply unimpressed. Its completely non-extensible (other than just overriding existing methods) and we're forced to use your
own customisation of the display. If i want to add additional rules via the client i have to use the .rules method for each field... etc etc.. not good, not using it.
We went down the path with jQuery Validation originally and found it didn't quite fit our needs, so we stopped development on it and moved it into Futures. It's not something that we support and is only included because it was present in interim builds of
MVC 2. It will definitely need a lot of work to be usable.
Marked as answer by ricka6 on Apr 09, 2010 09:34 PM
We went down the path with jQuery Validation originally and found it didn't quite fit our needs, so we stopped development on it and moved it into Futures. It's not something that we support and is only included because it was present in interim builds of MVC
2. It will definitely need a lot of work to be usable.
Ok fair enough. I never got into it before now. As I work through the issues I'll post any code i write here. At least the MMJQV.js file isn't particular big or hard to tweak. Starting with this to add support for the build in email & url (i havent tested
the url bit yet). Readers, u need to register these validators: DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DataTypeAttribute), typeof(DataTypeValidator));
public class DataTypeValidator : DataAnnotationsModelValidator<DataTypeAttribute>
{
public DataTypeValidator(ModelMetadata metadata, ControllerContext context, DataTypeAttribute attribute)
: base(metadata, context, attribute) {
this.message = attribute.ErrorMessage;
}
string message;
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
List<ModelClientValidationRule> rules = new List<ModelClientValidationRule>();
ModelClientValidationRule rule;
switch (Attribute.DataType)
{
case DataType.EmailAddress :
rule = new ModelClientValidationRule() { ErrorMessage = message, ValidationType = "email" };
rule.ValidationParameters.Add("true", "true");
rules.Add(rule);
break;
case DataType.Url :
rule = new ModelClientValidationRule() { ErrorMessage = message, ValidationType = "url" };
rule.ValidationParameters.Add("true", "true");
rules.Add(rule);
break;
}
return rules;
}
}
-- Sam Critchley
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
worldspawn[]
Contributor
6081 Points
1336 Posts
jQuery validation with data annotations in MVC2
Apr 08, 2010 02:49 AM|LINK
Hello, I am trying to make some sense of this validation stuff in MVC2. I followed various walkthroughs, all them for betas/rcs... and I cant' get anythign to happen. Note: THIS JQUERY - NOT THE MS AJAX STUFF!
I have referenced MicrosoftMvcJQueryValidation.js which i got got the mvc2 futures lib.
My model is:
public class User { protected User() { Roles = new List<Role>(); } public int UserId { get; set; } [Required(ErrorMessage = "")] [DataType(DataType.Text)] public string Username { get; set; } [DataType(DataType.Password)] public string Password { get; set; } [DisplayName("First name")] [DataType(DataType.Text)] [Required(ErrorMessage = "")] public string FirstName { get; set; } [DataType(DataType.Text)] [Required(ErrorMessage = "")] public string Surname { get; set; } [Required(ErrorMessage = "")] [DataType(DataType.EmailAddress)] public string Email { get; set; } [DisplayName("Disabled")] public bool CanLogin { get; set; } public DateTime? LastLogin { get; set; } public IList<Role> Roles { get; private set; } public string Name { get { return string.Format("{0} {1}", FirstName, Surname); } } public bool HasPermission(string permission) { foreach (Role r in Roles) { if (r.Permissions.Count(p => p.Name == permission) > 0) return true; } return false; } }The form bit of my ViewPage<User>:
<% Html.EnableClientValidation(); %> <% using(Html.BeginForm()){ %> <% Html.RenderPartial("errorHeading"); %> <% if (Model != null) { %><%= Html.HiddenFor(p => p.UserId)%><% } %> <div class="field req"> <label for="Username">Username</label> <%= Html.EditorFor(p => p.Username)%> </div> <div class="field req"> <%= Html.LabelFor(p=>p.Password) %> <%= Html.PasswordFor(p => p.Password)%> </div> <div class="field req"> <%= Html.LabelFor(p=>p.FirstName) %> <%= Html.EditorFor(p => p.FirstName)%> </div> <div class="field req"> <%= Html.LabelFor(p=>p.Surname) %> <%= Html.EditorFor(p => p.Surname)%> </div> <div class="field req"> <%= Html.LabelFor(p=>p.Email) %> <%= Html.EditorFor(p => p.Email)%> </div> <div class="field req"> <%= Html.LabelFor(p=>p.CanLogin) %> <%= Html.EditorFor(p => p.CanLogin)%> </div> <div class="field req"> <label for="Roles_0__RoleId">Role</label> <%= Html.DropDownList("Roles[0].RoleId", (IEnumerable<SelectListItem>)ViewData["RoleList"], "Select...")%> </div> <button type="submit">Save</button> <%= Html.ActionLink("Cancel", "List") %> <%} %>As far as I can see the only additional thing that is being rendered is this:
<script type="text/javascript"> //<![CDATA[ if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; } window.mvcClientValidationMetadata.push({"Fields":[],"FormId":"form0","ReplaceValidationSummary":false}); //]]> </script>Which is interesting/annoying for a few reasons. Firstly all of my annotations appear to have been ignored.
Secondly FormId isn't even marked as required, and as this rendered text was taken from a page loaded with a null Model (add mode) the FormId field isn't in the form.Finally, this doesn't even result in any actual validation occuring (on the client side). All the server side validation is working fine.Are there other methods I need to call...?
PS,
See I am using PasswordFor instead of EditorFor. Because someone at MS testing dropped the ball there. Yes EditorFor renders a password field, but it behaves differently to PasswordFor.. the value of the Password field is sent to the client (set in the value attribute of the field). Fail.
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
software development
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 03:50 AM|LINK
You're missing the calls to Html.ValidationMessage() (or Html.Validate()) which tell the validation system that you want to validate given properties.
So this:
<div class="field req"> <%= Html.LabelFor(p=>p.Password) %> <%= Html.PasswordFor(p => p.Password) %> </div>Should be this:
<div class="field req"> <%= Html.LabelFor(p=>p.Password) %> <%= Html.PasswordFor(p => p.Password) %> <%= Html.ValidationMessageFor(p => p.Password) %> </div>Or this:
<div class="field req"> <%= Html.LabelFor(p=>p.Password) %> <%= Html.PasswordFor(p => p.Password) %> <% Html.ValidateFor(p => p.Password) %> </div>The former shows the error message next to the field; the latter doesn't show any message at all (just marks the property as something to be validated, which means you should probably have a call to Html.ValidationSummary() somewhere to show all the error messages).
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 03:53 AM|LINK
Also, your [Required(ErrorMessage = "")] isn't legal. Either remove the set of ErrorMessage to get the default message, or set an actual message in the setter.
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 04:50 AM|LINK
I blogged about the Password issue. Believe it or not, it's not a bug. :)
Is ASP.NET MVC Exposing My Secrets?
worldspawn[]
Contributor
6081 Points
1336 Posts
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 05:11 AM|LINK
Hi,
Thanks for getting back to me. I figured out I needed a ValidateFor when i started rummaging around MVC2 source code looking for where meta data was populated from. Seems reasonable, however the "" ErrorMessage thing isn't! Not only should this be allowed but having it there causes all the other attributes to silently fail. For example if i have ErrorMessage (with "") and DisplayName on a property neither of the attributes manifest themselves.
Jquery validate is very extensible and I have a pretty non default customisation of the error display element, I personally only show error messages for anything other than required field errors. Required field errors are indicated by highlighting the field in question. You are basically telling me I can't do that because MS couldn't envisage a scenario where that would be wanted. You envisioned wrong!
I will now probably end up setting the errormessage to something like '--' and strip it out on the client...
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
software development
worldspawn[]
Contributor
6081 Points
1336 Posts
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 05:16 AM|LINK
Heres my validate customisation. Css not included:
var validatorDefaults = { errorPlacement: function(error, element) { var errorContainer = this.errorContainer.split(',')[0]; if (error.html().length > 0 && error.html() != '--') { var li = $('<li></li>'); li.appendTo(errorContainer); error.appendTo(li); li.attr('for', element.attr('id')); } }, highlight: function(element, errorClass) { if (element.tagName.toUpperCase() == "INPUT" && element.type.toUpperCase() == "RADIO") { var name = element.name; var elements = $('input[name=' + name + ']'); elements.each(function(index, item) { $(element.form).find(".inputlist label[for=" + item.id + "]").addClass('highlightlabel'); }); } else $(element).addClass(errorClass); }, unhighlight: function(element, errorClass) { if (element.tagName.toUpperCase() == "INPUT" && element.type.toUpperCase() == "RADIO") { var name = element.name; var elements = $('input[name=' + name + ']'); elements.each(function(index, item) { $(element.form).find(".inputlist label[for=" + item.id + "]").removeClass('highlightlabel'); }); } else $(element).removeClass(errorClass); }, errorContainer: "#messagebox, #errorHeading", errorClass: "input-validation-error" }I render this above my form:
<h6 id="errorHeading"> There are errors in the information entered</h6> <ul id="messagebox"> <% foreach (var error in ViewData.ModelState.Where(p => p.Value.Errors.Count > 0)) { foreach (var x in error.Value.Errors.Where(p => p.ErrorMessage.Trim().Length > 0)) { %> <li> <label class="input-validation-error" for="<%= error.Key.Replace(".", "_").Replace("[", "_").Replace("]", "_") %>"> <%= x.ErrorMessage %></label></li> <% } } %> </ul>I'm halfway through implementing this validation stuff so maybe my foreach loop can go... see how the validation thing works when/if i get it working.
Also shouldn't DataType(DataType.Email) trigger a email : true rule? Likewise for URL..
Edit: after looking at MicrosoftMvcJQueryValidation.js I have to say I am deeply unimpressed. Its completely non-extensible (other than just overriding existing methods) and we're forced to use your own customisation of the display. If i want to add additional rules via the client i have to use the .rules method for each field... etc etc.. not good, not using it.
"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
software development
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 06:26 AM|LINK
It sounds like what you want is outside the architecture of our system. It's hard to follow exactly what you're trying to do, but you should consider providing a full example as a feature request.
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 06:30 AM|LINK
We went down the path with jQuery Validation originally and found it didn't quite fit our needs, so we stopped development on it and moved it into Futures. It's not something that we support and is only included because it was present in interim builds of MVC 2. It will definitely need a lot of work to be usable.
worldspawn[]
Contributor
6081 Points
1336 Posts
Re: jQuery validation with data annotations in MVC2
Apr 08, 2010 07:06 AM|LINK
Ok fair enough. I never got into it before now. As I work through the issues I'll post any code i write here. At least the MMJQV.js file isn't particular big or hard to tweak. Starting with this to add support for the build in email & url (i havent tested the url bit yet). Readers, u need to register these validators: DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DataTypeAttribute), typeof(DataTypeValidator));
public class DataTypeValidator : DataAnnotationsModelValidator<DataTypeAttribute> { public DataTypeValidator(ModelMetadata metadata, ControllerContext context, DataTypeAttribute attribute) : base(metadata, context, attribute) { this.message = attribute.ErrorMessage; } string message; public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { List<ModelClientValidationRule> rules = new List<ModelClientValidationRule>(); ModelClientValidationRule rule; switch (Attribute.DataType) { case DataType.EmailAddress : rule = new ModelClientValidationRule() { ErrorMessage = message, ValidationType = "email" }; rule.ValidationParameters.Add("true", "true"); rules.Add(rule); break; case DataType.Url : rule = new ModelClientValidationRule() { ErrorMessage = message, ValidationType = "url" }; rule.ValidationParameters.Add("true", "true"); rules.Add(rule); break; } return rules; } }"Wise man say 'forgiveness is divine, but never pay full price for late pizza." - TMNT
software development