jQuery Unobtrusive client-side validation of custo

2019-02-09 23:47发布

问题:

I have created a custom validation attribute which works server-side (after form is posted) but I cannot get the validation to work client-side.

The custom attribute is:

public class ReasonableAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        return Approval.IsNumberReasonable(value.ToString());
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        ModelClientValidationRule rule = new ModelClientValidationRule();
        rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
        rule.ValidationType = "reasonable";
        yield return rule;
    }

}

The IsNumberReasonable() function just does some checks on the fields input to make sure what they entered could reasonably be an Approval number (e.g. not empty, or "Unknown" or something like that) returning a bool true/false.

My Model then contains:

[Display(Name = "Approval Number"]
[Reasonable(ErrorMessage = "Please enter a reasonable {0}")]
public String ApprovalNumber { get; set; }

and the View contains:

@Html.LabelFor(model => model.ApprovalNumber, new { @class = "control-label col-md-3" })
<div class="col-md-9 append field">
 @Html.TextBoxFor(model => model.ApprovalNumber, new { @class = "input text" })
 @Html.ValidationMessageFor(model => model.ApprovalNumber)
</div>

in the JavaScript I have tried adding:

$.validator.unobtrusive.adapters.addBool("reasonable", "required");
// OR
$.validator.unobtrusive.adapters.add("reasonable");

and: (outside of document.ready)

(function ($) {
            $.validator.addMethod('reasonable', function (value, element, params) {
                return value != '';
            }, 'Clientside Should Not Postback');
            $.validator.unobtrusive.adapters.addBool('reasonable');
        })(jQuery);

in various combinations but have not been able to get it to work.

I have got

<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

included in the View.

When the form is submitted, client-side validation works for other fields (required ones etc) but the validation message next to the ApprovalNumber never appears/does not perform my custom validation.

Any pointers in the right direction are appreciated. thanks.

I also have

<configuration>
  <appSettings>  
   <add key="ClientValidationEnabled" value="true" />
   <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
</configuration>

Already in the Web.config file.

Edit 2: as per chenZ's post: The html for the input field is:

<input class="input text valid" data-val="true" data-val-reasonable="Please enter a reasonable Approval Number" data-val-required="Approval Number is Required." id="ApprovalNumber" name="ApprovalNumber" type="text" value="">

Tried updating the bottom of my view with:

<script src="~/Scripts/jquery.validate.js"></script>
<script type="text/javascript">
    (function ($) {
        $.validator.addMethod('reasonable', function (value, element, params) {
            return value != '';
        }, 'Clientside Should Not Postback');
    })(jQuery);
    </script>

    <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/Create.js"></script>

where the end of jquery.validate.unobtrusive.js now contains:

    $(function () {
        $.validator.unobtrusive.adapters.addBool('reasonable');
        $("#formID").removeData("unobtrusiveValidation").removeData("validator");
        $jQval.unobtrusive.parse(document);
    });
}(jQuery));

and create.js contains:

$.validator.unobtrusive.adapters.addBool("mandatory", "required"); // another custom attribute for checkbox validation
    var v = $("#formID").validate({
        errorClass: "warning",
        onkeyup: false,
        onblur: false,
    });

the v variable is used further down for the submit button:

$("#SubmitButton").click(function () {
    if (v.form()) {
          // The form passed validation so continue..
        } else {
            // Validation failed, do something else..
        }
});

I did some further testing ans it appears to be something to do with the:

(function ($) {
            $.validator.addMethod('reasonable', function (value, element, params) {
                return value != '';
            }, 'Clientside Should Not Postback');
        })(jQuery);

when I change it to:

(function ($) {
            $.validator.addMethod('reasonable', function (value, element, params) {
                return value != 'unknown';
            }, 'Clientside Should Not Postback');
        })(jQuery);

it works, but only if I type 'unknown' into the input box, anything else passes.. similarly if I put return value != 'jhdsfkhsdkfj'; it only validates when I input jhdsfkhsdkfj.

so it appears that it is using that as the rule? instead of my server-side IsNumberReasonable() function.

hope that helps in troubleshooting.

回答1:

I haven't tried it, so I'm not sure, but i think there are 2 places where you may be wrong.

1.rule.ValidationType = "reasonable";
Check your html, find the input and is there an attr data-val-reasonalbe or reasonale

$.each(this.adapters, function () {
            var prefix = "data-val-" + this.name,

in jquery.validate.unobtrusive.js, line 173, you can find this, so it must be a data-val-xxxx

2.in jquery.validate.unobtrusive.js end of file you can find

$(function () {
    $jQval.unobtrusive.parse(document);
});

so, when the page is loaded, all form set validate

This is a difficult thing. If your code is like

<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
.....
(function ($) {
        $.validator.addMethod('reasonable', function (value, element, params) {
            return value != '';
        }, 'Clientside Should Not Postback');
        $.validator.unobtrusive.adapters.addBool('reasonable');
    })(jQuery);

When you do unobtrusive.parse(document); your valid method reasonable doesn't exist, and adapter doesn't exist exist either, so your form valid doesn't have a rule reasonable

If you change the code to like this

<script src="~/Scripts/jquery.validate.js"></script>
(function ($) {
        $.validator.addMethod('reasonable', function (value, element, params) {
            return value != '';
        }, 'Clientside Should Not Postback');
        $.validator.unobtrusive.adapters.addBool('reasonable');
    })(jQuery);
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

the valid method can add, but the $.validator.unobtrusive is undefined

I think you should do more

<script src="~/Scripts/jquery.validate.js"></script>
(function ($) {
        $.validator.addMethod('reasonable', function (value, element, params) {
            return value != '';
        }, 'Clientside Should Not Postback');
    })(jQuery);
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

and put the

$.validator.unobtrusive.adapters.addBool('reasonable');

in to jquery.validate.unobtrusive.js, and make sure it runs before

$(function () {
    $jQval.unobtrusive.parse(document);
});

I'm not sure if it will work, maybe I'll try later. Hope this can help you.

update I tried it. In point 2, you can use code

$(function () {
    $("#formid").removeData("unobtrusiveValidation").removeData("validator");
    $.validator.unobtrusive.parse(document);
});

Put this code after your add valid method, this will clear valid setting and parse again with your custom rules

update 2

$("#a").validate({});//first time call is useful
$("#a").validate({});//second time call is useless

also to unobtrusive.parse(...)

jquery.validate.js line 41

var validator = $.data( this[0], "validator" );
if ( validator ) {
    return validator;
}

and jquery.validate.unobtrusive.js line 99

result = $form.data(data_validation),
    ...
    if (!result) { ....

when <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
end of this file, called unobtrusive.parse(document), it will append a data to form. If it called before you setup code, the validate setting is without your setup. you can removeData(...), and run unobtrusive.parse(document) by your code again like

<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/setting.js"></script>
<script>
  $(function(){ $("#formid").removeData(...).removeData(...); 
  ....unobtrusive.parse(document)});
</script>

validate plugin use data name "validator" and unobtrusive use data name "unobtrusiveValidation".

I'm really sorry for my poor English, hope you can understand.



回答2:

Add this to your Web.config

<configuration>
  <appSettings>  
   <add key="ClientValidationEnabled" value="true" />
   <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
</configuration>

Cool then try modifying your rule to use the rule below.

$.validator.unobtrusive.adapters.add("reasonable", ["reasonable"], function (options) {
options.rules["reasonable"] = "#" + options.params.reasonable;
options.messages["reasonable"] = options.message;});


回答3:

Since I landed here when looking into my issue, I'd like to share that if you're using any 3rd party libraries for controls, such as DevExpress with option embed client libraries, then they may actually re-include the validation scripts, which could lead to your adapter being properly added only to be later lost again. In my case, I had to move my .js file creating the client side unobtrusive validator adapter down below these other offending scripts. I'd also want to either consider dropping my own inclusion of said files, or configuring my 3rd party library not to include them, to prevent such confusion.