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

时间:2016-12-22 19:30:29

标签: javascript c# asp.net-core asp.net-core-mvc

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

我使用这篇文章作为参考: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">

2 个答案:

答案 0 :(得分:3)

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

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

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

以下是我的“EffectiveRequired”验证器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提供动力的主文件)中,我实例化上面验证器的新副本(从而调用构造函数,将自定义验证器添加到内存中的验证器对象)< em>在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>

答案 1 :(得分:0)

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

归功于@ Loni2Shoes