Bhushan Shah
Bhushan Shah

Reputation: 1038

How to change MVC custom client-side validation error message at runtime using jQuery?

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

Answers (1)

Maksym Strukov
Maksym Strukov

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

Related Questions