自定义验证不触发客户端

Posted

技术标签:

【中文标题】自定义验证不触发客户端【英文标题】:Custom validation not firing client-side 【发布时间】:2017-05-08 12:12:40 【问题描述】:

如果另一个属性具有指定的值,我正在编写一个自定义属性以要求视图模型中的一个属性。

我用这篇文章作为参考:RequiredIf Conditional Validation Attribute

但在 IClientModelValidator 的 .NET Core 修订版中遇到了问题。具体来说,服务器端验证按预期工作,ModelState.IsValid 返回 false,并且 ModelState 错误包含我的自定义错误代码。在验证器的不同版本之间进行翻译时,我觉得我遗漏了一些东西。

旧的(有效的)解决方案具有以下特点:

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;

基于此处概述的对 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;
    

这些正在按预期调用,并且值已正确填充,但以下 JS 被忽略。让我怀疑我在两者之间遗漏了一些东西。

    $.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;
    );

有什么想法吗?

编辑:只是为了消除对服务器端是否正常工作的怀疑,而问题几乎肯定在于客户端,这里是为装饰字段生成的 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">

【问题讨论】:

您是否在视图中添加了对 jqueryvalidate.js 的脚本引用? 您能在视图中向我们展示您的脚本引用吗? 我遇到了同样的问题。你有运气吗? 【参考方案1】:

所以我的设置和结果与原来的提问者相同。通过单步执行自定义验证器被触发和未触发的项目,我能够确定当页面最初加载时,jquery.validate.js 将验证器对象附加到表单。工作项目的验证器包含我创建的自定义验证器的密钥。无效的验证器缺少该密钥(后来添加并在我发布表单时可用)。

不幸的是,由于验证器对象已经创建并附加到表单没有我的自定义验证器,它从来没有达到那个功能。解决此问题的关键是将我的两个 JS 函数 移出 jQuery 就绪函数,尽可能靠近我的主脚本顶部(就在我设置我的 jQuery 验证器默认值之后)。我希望这对其他人有帮助!

我的项目是用 TypeScript 编写的,所以我的结构有点不同,但实际添加验证器的 javascript 保持不变。

这是我的“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
    

然后在我的 boot-client.ts 文件(为我的应用程序的 JavaScript 提供支持的主文件)中,我实例化了上面验证器的一个新副本(因此调用了将自定义验证器添加到内存中的验证器对象的构造函数)document.ready 之外:

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();
    

作为一个不使用 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>

【讨论】:

您能否在答案中添加一些代码示例? 我在上面添加了一些代码,尽管是在 TypeScript 中。希望这个概念应该能够从我提供的内容中理解。 你是明星@Loni2Shoes,我花了将近 5 个小时,然后你关于将代码移到文档之外的评论解决了这个问题。非常感谢。【参考方案2】:

解决这个问题的关键是将我的两个 JS 函数移到 jQuery 就绪函数之外,尽可能靠近我的主脚本顶部(就在我设置我的 jQuery 验证器默认值之后)。我希望这对其他人有帮助!

归功于@Loni2Shoes

【讨论】:

以上是关于自定义验证不触发客户端的主要内容,如果未能解决你的问题,请参考以下文章

自定义验证属性不触发 jQuery 验证

自定义客户端验证 jQuery 适配器不会在 ASP.Net Core 中触发

自定义不显眼的验证方法未按照文档触发

Web API 验证不使用自定义模型绑定器触发

ASP.Net 自定义客户端验证

自定义属性的 ASP.NET MVC 客户端验证