简体   繁体   English

自定义验证不会触发客户端

[英]Custom validation not firing client-side

I am writing a custom attribute to require a property in a viewmodel if another property has a specified value. 我正在编写一个自定义属性,以便在另一个属性具有指定值时要求viewmodel中的属性。

I used this post for reference: RequiredIf Conditional Validation Attribute 我使用这篇文章作为参考: RequiredIf Conditional Validation Attribute

But have been encountering issues with the .NET Core revisions for IClientModelValidator. 但是一直遇到IClientModelValidator的.NET Core修订版的问题。 Specifically, the server side validation works as expected with ModelState.IsValid returning false, and ModelState errors containing my custom error codes. 具体来说,服务器端验证按预期工作,ModelState.IsValid返回false,ModelState错误包含我的自定义错误代码。 I feel that I am missing something when translating between the differing versions of validator. 在不同版本的验证器之间进行翻译时,我觉得我遗漏了一些东西。

The old (working) solution has the following: 旧(工作)解决方案具有以下内容:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
    ControllerContext context)
{
    var rule = new ModelClientValidationRule
    {
        ErrorMessage = ErrorMessageString,
        ValidationType = "requiredif",
    };
    rule.ValidationParameters["dependentproperty"] =
        (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
    rule.ValidationParameters["desiredvalue"] = DesiredValue is bool
        ? DesiredValue.ToString().ToLower()
        : DesiredValue;

    yield return rule;
}

Based on the changes to IClientModelValidator outlined here: https://github.com/aspnet/Announcements/issues/179 I have written the following methods: 基于此处概述的IClientModelValidator的更改: https//github.com/aspnet/Announcements/issues/179我编写了以下方法:

    public void AddValidation(ClientModelValidationContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        MergeAttribute(context.Attributes, "data-val", "true");

        var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
        MergeAttribute(context.Attributes, "data-val-requiredif", errorMessage);

        MergeAttribute(context.Attributes, "data-val-requiredif-dependentproperty", PropertyName);

        var desiredValue = DesiredValue.ToString().ToLower();
        MergeAttribute(context.Attributes, "data-val-requiredif-desiredvalue", desiredValue);
    }

    private bool MergeAttribute(
        IDictionary<string, string> attributes,
        string key,
        string value)
    {
        if (attributes.ContainsKey(key))
        {
            return false;
        }
        attributes.Add(key, value);
        return true;
    }

These are being called as expected, and values are properly populated, yet the following JS is ignored. 这些是按预期调用的,并且值已正确填充,但忽略了以下JS。 Leaving me to suspect I am missing something between the two. 让我怀疑我错过了两者之间的某些东西。

    $.validator.addMethod("requiredif", function (value, element, parameters) {
        var desiredvalue = parameters.desiredvalue;
        desiredvalue = (desiredvalue == null ? "" : desiredvalue).toString();
        var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
        var actualvalue = {}
        if (controlType === "checkbox" || controlType === "radio") {
            var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
            actualvalue = control.val();
        } else {
            actualvalue = $("#" + parameters.dependentproperty).val();
        }
        if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
            var isValid = $.validator.methods.required.call(this, value, element, parameters);
            return isValid;
        }
        return true;
    });
    $.validator.unobtrusive.adapters.add("requiredif", ["dependentproperty", "desiredvalue"], function (options) {
        options.rules["requiredif"] = options.params;
        options.messages["requiredif"] = options.message;
    });

Any ideas? 有任何想法吗?

EDIT: Just to erase doubt that the server side is working properly and the issue almost certainly lies client side, here is a snip of the generated HTML for a decorated field: 编辑:只是为了消除服务器端正常工作的疑问,问题几乎肯定在客户端,这里是一个装饰字段生成的HTML片段:

<input class="form-control" type="text" data-val="true" data-val-requiredif="Profession Other Specification is Required" data-val-requiredif-dependentproperty="ProfessionTypeId" data-val-requiredif-desiredvalue="10" id="ProfessionOther" name="ProfessionOther" value="" placeholder="Please Specify Other">

So I had the same setup and same result as the original questioner. 所以我有与原始提问者相同的设置和相同的结果。 By stepping through a project where custom validators were being fired and where they weren't, I was able to determine that when the page is initially loaded, jquery.validate.js attaches a validator object to the form. 通过逐步调试自定义验证器的项目以及它们不被触发的位置,我能够确定在最初加载页面时,jquery.validate.js将验证器对象附加到表单。 The validator for the working project contained the key for the custom validator I had created. 工作项目的验证器包含我创建的自定义验证器的密钥。 The validator for the one that did not work was missing that key (which was later added and available at the time I was posting my form). 没有工作的验证器缺少该密钥(后来在我发布表单时添加并可用)。

Unfortunately, as the validator object had already been created and attached to the form without my custom validator, it never reached that function. 不幸的是,由于验证器对象已经创建并附加到没有我的自定义验证器的表单,它从未到达该函数。 The key to solving this issue was to move my two JS functions outside of the jQuery ready function, as close to the top of my main script as possible (just after I set my jQuery validator defaults). 解决这一问题的关键是要动我的两个JS功能jQuery的准备功能 ,尽量靠近我的主要脚本的顶部越好(就在我把我的jQuery的验证器的默认值)。 I hope this helps someone else! 我希望这有助于其他人!

My project is written in TypeScript, so my structure is a bit different but the JavaScript for actually adding the validator remains unchanged. 我的项目是用TypeScript编写的,所以我的结构有点不同,但实际添加验证器的JavaScript保持不变。

Here is the code for my "SometimesRequired" validator Typescript class: 这是我的“SometimesRequired”验证器Typescript类的代码:

export class RequiredSometimesValidator {
    constructor() {
        // validator code starts here
        $.validator.addMethod("requiredsometimes", function (value, element, params) {
            var $prop = $("#" + params);
            // $prop not found; search for a control whose Id ends with "_params" (child view)
            if ($prop.length === 0) 
                $prop = $("[id$='_" + params + "']");

            if ($prop.length > 0) {
                var ctrlState = $prop.val();
                if (ctrlState === "EditableRequired" && (value === "" || value === "Undefined"))
                    return false;
            }
            return true;
        });

        $.validator.unobtrusive.adapters.add("requiredsometimes", ["controlstate"], function (options) {
            options.rules["requiredsometimes"] = options.params["controlstate"];
            options.messages["requiredsometimes"] = options.message;
        });
        // validator code stops here
    }
}

Then in my boot-client.ts file (the main file which powers my application's JavaScript), I instantiate a new copy of the validator above (thus calling the constructor which adds the custom validator to the validator object in memory) outside of document.ready : 然后在我的boot-client.ts文件(为我的应用程序的JavaScript提供动力的主文件)中,我在文档之外实例化上面的验证器的新副本(因此调用构造函数,该构造函数将自定义验证器添加到内存中的验证器对象) 。准备好了

export class Blueprint implements IBlueprint {
    constructor() {
        // this occurs prior to document.ready
        this.initCustomValidation();

        $(() => { 
            // document ready stuff here
        });
    }
    private initCustomValidation = (): void => {
        // structure allows for load of additional client-side validators
        new RequiredSometimesValidator();
    }
}

As a very simple example not using TypeScript, you should be able to do this: 作为一个不使用TypeScript的简单示例,您应该能够这样做:

<script>
    $.validator.addMethod("requiredsometimes", function (value, element, params) {
        var $prop = $("#" + params);
        // $prop not found; search for a control whose Id ends with "_params" (child view)
        if ($prop.length === 0) 
            $prop = $("[id$='_" + params + "']");

        if ($prop.length > 0) {
            var ctrlState = $prop.val();
            if (ctrlState === "EditableRequired" && (value === "" || value === "Undefined"))
                return false;
        }
        return true;
    });

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

    $(function() {
        // document ready stuff
    });

</script>

The key to solving this issue was to move my two JS functions outside of the jQuery ready function, as close to the top of my main script as possible (just after I set my jQuery validator defaults). 解决这个问题的关键是将我的两个JS函数移到jQuery ready函数之外,尽可能靠近我的主脚本顶部(就在我设置jQuery验证器默认值之后)。 I hope this helps someone else! 我希望这有助于其他人!

Credit goes to @Loni2Shoes 归功于@ Loni2Shoes

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM