Reputation: 1038
I am using MVC5, and I have a ViewModel for my View, which contains a simple form with the following fields:
MinFirstNameLength
FirstName
MinLastNameLength
LastName
Now, I wish to apply a validation rule on FirstName
, based on the value of MinFirstNameLength
, and similarly for LastName
using MinLastNameLength
. I want to do this on the client-side too.
So, I used MVC's unobtrusive client side validation feature. I made a custom validation attribute, implementing the IClientValidatable
interface. The GetClientValidationRules
method looks like this:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string ErrorMessage = ErrorMessageString;
ModelClientValidationRule NameMinLengthRule = new ModelClientValidationRule();
NameMinLengthRule.ErrorMessage = ErrorMessage;
NameMinLengthRule.ValidationType = "nameminlength";
NameMinLengthRule.ValidationParameters.Add("minlengthpropname", MinLengthPropName);
yield return NameMinLengthRule;
}
This validation attribute is applied on the FirstName
and LastName
properties like this:
[NameMinLength("FirstNameMinLength",ErrorMessage = "First Name must be at least {0} characters"]
public string FirstName { get; set; }
[NameMinLength("LastNameMinLength",ErrorMessage = "Last Name must be at least {0} characters"]
public string LastName { get; set; }
Also, I have the client side validation functions in a .js file elsewhere, which looks like this:
$.validator.addMethod("nameminlength",
function (
value,
element,
params
) {
return value.length >= parseInt($(params).val());
});
$.validator.unobtrusive.adapters.add("nameminlength", ["minlengthpropname"], function (options) {
var paramField = "#" + options.params.minlengthpropname;
options.rules["nameminlength"] = paramField;
var errormessage = options.message;
options.messages["nameminlength"] = errormessage;
});
My question is, how do I assign the value in the textbox for MinFirstNameLength
to the placeholder {0}
in my error message for FirstName
, so that the error message reads First Name must be at least [value] characters
?
I tried adding modifying my $.validator.unobtrusive.adapters.add
method as follows:
$.validator.unobtrusive.adapters.add("nameminlength", ["minlengthpropname"], function (options) {
var paramField = "#" + options.params.minlengthpropname;
options.rules["nameminlength"] = paramField;
var errormessage = options.message;
options.messages["nameminlength"] = errormessage;
$(paramField).blur(function () {
// change error message in the 'data-val-nameminlength' attribute of the HTML element on which validation is applied
var newErrorMessage = options.messages["nameminlength"].replace("{0}", $(paramField).val());
$(options.element).attr("data-val-nameminlength", newErrorMessage);
// change error message inside the error message span generated for displaying this error
$(options.element).siblings(".field-validation-valid").html(newErrorMessage);
});
});
But both these tricks didn't work. The HTML markup did change to modify the error appropriately, but this change wasn't reflected in the page.
Currently, the error message I see is:
First Name must be at least #FirstNameMinLength characters
The placeholder is replaced automatically by the id of the control which is used as a parameter in the validation rule.
Where does this error message come from? How do I modify it at runtime?
Upvotes: 2
Views: 6770
Reputation: 2689
I think you should just format your message on server side in the IsValid method of your NameMinLength validation attribute. Here's a small example on how to do it.
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var conditionalPropertyInfo = validationContext.ObjectType.GetProperty(this.MinLengthPropName);
var conditionalPropertyValue = conditionalPropertyInfo.GetValue(validationContext.ObjectInstance, null);
this.ErrorMessage = string.Format(this.ErrorMessage, conditionalPropertyValue.ToString())
// YOUR OTHER CODE HERE
}
This should replace the placeholder with the correct value from MinLengthPropName property. In this case the error message will be formatted before moving it to client side. So no additional logic required for this.
EDIT: Hm, just thought that you might want to validate based on user input which is really wierd. Actually the fact that you might have different min length restrictions for the same field doesn't make sense to me but as long as it' not based on user input it is much more secure.
UPDATED WITH CLIENT SIDE SOLUTION:
I did a simple test with a similar attribute and it works for me
Edit Model:
[MinCustomLength("MinLenthDestURI", "Dest URI must be at least {0} characters")]
public string DestinationURI { get; set; }
public int MinLenthDestURI { get; set; }
Attribute code:
public class MinCustomLengthAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
public MinCustomLengthAttribute(String propertyName, String errormessage)
{
this.PropertyName = propertyName;
this.ErrorMessage = errormessage;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
// Just for test server side validation always returns Success
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var modelClientValidationRule = new ModelClientValidationRule
{
ValidationType = "mincustomlength",
ErrorMessage = this.ErrorMessage
};
modelClientValidationRule.ValidationParameters.Add("prop", PropertyName);
yield return modelClientValidationRule;
}
}
Client side code:
$.validator.addMethod("mincustomlength", function (value, element, params) {
var conditionalId = $.validator.getId(element, params.prop);
var minLength = parseInt($("#" + conditionalId).val());
if (value.length < minLength) {
var message = $(element).attr('data-val-mincustomlength');
$.validator.messages.mincustomlength = $.format(message, minLength);
return false;
}
return true;
});
$.validator.unobtrusive.adapters.add('mincustomlength', ['prop'], function (options) {
options.rules['mincustomlength'] = options.params;
if (options.message != null) {
$.validator.messages.mincustomlength = options.message;
}
});
This will replace {0} with a value from MinLenthDestURI textbox when validation is done.
Hope it helps!
Upvotes: 2