关于 c#:针对文本框控件链接必需字段和正则表达式验证器的问题

Issue with chaining Required Field and Regular Expression validators for Textbox controls

我正在尝试使用 ASP.net 实现表单验证,我已经尝试了此处建议的所有解决方案,但迄今为止最好的解决方案是在 aspsnippets.com 上。

我的代码如下:

1
2
<asp:RegularExpressionValidator runat="server" ControlToValidate="tEMail"
ValidationExpression="\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"/>

Javascript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script type="text/javascript">
function WebForm_OnSubmit() {
    if (typeof (ValidatorOnSubmit) =="function" && ValidatorOnSubmit() == false)
    {
        for (var i in Page_Validators) {
            try {
                var control =
                document.getElementById(Page_Validators[i].controltovalidate);
                if (!Page_Validators[i].isvalid) {
                    control.className ="error";
                } else {
                    control.className ="";
                }
            } catch (e) { }
        } return false;
    } return true;
}
function WebForm_OnBlur() {
    for (var i in Page_Validators) {
        try {
            var control =
            document.getElementById(Page_Validators[i].controltovalidate);
            if (!Page_Validators[i].isvalid) {
                control.className ="error";
            } else {
                control.className ="";
            }
        } catch (e) { }
    } return false;
}

问题是电子邮件字段仅验证正则表达式。如果我更改验证器的顺序,它只会验证所需的表达式。

可能的问题是代码循环遍历所有验证器,但不会一次比较引用相同控件的验证器。这会导致只在控件上应用最后一个验证器条件。


The possible problem is that the code loops through all the validators but does not compare the ones that reference the same control at once. This causes only the last validator condition to be applied on the control.

是的,这确实是问题所在。要修复它,您可以执行以下操作:

WebForm_OnBlur函数中,循环遍历失去焦点的控件关联的验证器(而不是页面上的所有验证器),只有当所有验证器都有效时才清除className属性:

1
2
3
4
5
6
7
8
9
function WebForm_OnBlur(control) {
    for (var i = 0; i < control.Validators.length; i++) {
        if (!control.Validators[i].isvalid) {
            control.className ="error";
            return;
        }
    }
    control.className ="";
}

TextBox 控件的 onblur 属性中,将 this 作为参数传递给 WebForm_OnBlur:

1
 

WebForm_OnSubmit 函数中,为每个具有关联验证器的控件调用 WebForm_OnBlur

1
2
3
4
5
6
7
8
9
10
11
function WebForm_OnSubmit() {
    if (typeof(ValidatorOnSubmit) ==="function" && ValidatorOnSubmit() === false) {
        for (var i = 0; i < Page_Validators.length; i++) {
            var control = document.getElementById(Page_Validators[i].controltovalidate);
            if (Page_Validators[i] === control.Validators[0]) // minor optimization
                WebForm_OnBlur(control);
        }
        return false;
    }
    return true;
}

通过替换下面的代码片段解决了这个问题。为了更正,我们必须遍历一个控件的所有验证器,然后我们应该决定它是否必须用错误类标记。在此之后,您的代码将按预期工作。

更换循环

1
2
3
4
5
6
7
8
9
10
11
 for (var i in Page_Validators) {
    try {
        var control =
        document.getElementById(Page_Validators[i].controltovalidate);
        if (!Page_Validators[i].isvalid) {
            control.className ="error";
        } else {
            control.className ="";
        }
    } catch (e) { }
}

使用以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 for (var j in Page_Validators) {
        try {
            var control =
                        document.getElementById(Page_Validators[j].controltovalidate);
            var IsError = false;
            for (var i in control.Validators) {

                if (!control.Validators[i].isvalid) {
                    IsError = true;
                }
            }

            if (IsError)
                control.className ="error";
            else
                control.className ="";
        } catch (e) { }
    }

我刚刚运行了它,它运行良好 :) 试试这个解决方案!


除了@MichaelLiu,您还可以制作自己的继承自 CustomValidator 类的验证器,并更改验证器的呈现方式以使其更易于使用。

例如:

验证器.cs

注意我们如何添加 CssControlErrorClass 的属性。我们将在应用具有相关无效输入的类时使用它。

我们还设置了其他属性,因此您不必每次都设置它们,ClientValidationFunctionValidateEmptyText

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class RequiredFieldValidator : CustomValidator
{
    public string CssControlErrorClass { get; set; }

    public RequiredFieldValidator()
    {
        ClientValidationFunction ="validators.required";
        ValidateEmptyText = true;
    }

    public string InitialValue
    {
        get
        {
            object o = ViewState["InitialValue"];
            return ((o == null) ? String.Empty : (string)o);
        }
        set
        {
            ViewState["InitialValue"] = value;
        }
    }

    protected override void Render(HtmlTextWriter writer)
    {
        //Have to add attributes BEFORE the beginning tag is written to the stream
        writer.AddAttribute("data-errorClass", CssControlErrorClass);
        writer.AddAttribute("data-for", GetControlRenderID(ControlToValidate));

        base.Render(writer);
    }



    protected override bool EvaluateIsValid()
    {
        //Default implementation of the RequiredFieldValidation validator
        string controlValue = GetControlValidationValue(ControlToValidate);

        if (controlValue == null)
        {
            return true;
        }

        var result = (!controlValue.Trim().Equals(InitialValue.Trim()));

        //Check to see if validation failed, if it did, add the class to the control to validate
        if (!result)
        {
            var control = (WebControl) NamingContainer.FindControl(ControlToValidate);

            //Didn't look into it too much, but the validators fire twice for some reason
            if(!control.CssClass.Contains(CssControlErrorClass)) control.CssClass +="" + CssControlErrorClass;
        }

        return result;
    }
}


public class RegularExpressionValidator : CustomValidator
{
    public string CssControlErrorClass { get; set; }

    public string ValidationExpression
    {
        get
        {
            object o = ViewState["ValidationExpression"];
            return ((o == null) ? String.Empty : (string)o);
        }
        set
        {
            try
            {
                Regex.IsMatch(String.Empty, value);
            }
            catch (Exception e)
            {
                throw new HttpException(string.Format("{0} - {1}","Validator_bad_regex", value), e);
            }
            ViewState["ValidationExpression"] = value;
        }
    }

    public RegularExpressionValidator()
    {
        ClientValidationFunction ="validators.regex";
    }

    protected override void Render(HtmlTextWriter writer)
    {
        //Have to add attributes BEFORE the beginning tag is written to the stream
        writer.AddAttribute("data-errorClass", CssControlErrorClass);
        writer.AddAttribute("data-regex", ValidationExpression);
        writer.AddAttribute("data-for", GetControlRenderID(ControlToValidate));

        base.Render(writer);
    }

    protected override bool EvaluateIsValid()
    {
        //Default implementation of the RegularExpressionFieldvalidator
        string controlValue = GetControlValidationValue(ControlToValidate);
        if (controlValue == null || controlValue.Trim().Length == 0)
        {
            return true;
        }

        try
        {
            Match m = Regex.Match(controlValue, ValidationExpression);
            var result = (m.Success && m.Index == 0 && m.Length == controlValue.Length);

            //Check to see if validation failed, if it did, add the class to the control to validate
            if (!result)
            {
                var control = (WebControl) NamingContainer.FindControl(ControlToValidate);

                //Didn't look into it too much, but the validators fire twice for some reason
                if (!control.CssClass.Contains(CssControlErrorClass)) control.CssClass +="" + CssControlErrorClass;
            }
            return result;
        }
        catch
        {
            return true;
        }
    }
}

验证器.js

由于在前面的类中我们预先定义了 javascript 函数,我们可以像这样添加一个简单的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var v = window.validators = window.validators || {
    errorControlAttributeName:"data-for",
    errorClassAttributeName:"data-errorClass",
    regexAttributeName:"data-regex",

    required: function(src, args) {
        var controlId = src.getAttribute(v.errorControlAttributeName),
            errorClass = src.getAttribute(v.errorClassAttributeName),
            input = document.getElementById(controlId);

        var isValid = (args.Value !=="");

        v._toggleInputErrorState(input, errorClass, isValid);

        args.IsValid = isValid;
        return;
    },

    regex: function(src, args) {
        var controlId = src.getAttribute(v.errorControlAttributeName),
            errorClass = src.getAttribute(v.errorClassAttributeName),
            regexString = src.getAttribute(v.regexAttributeName),
            input = document.getElementById(controlId),
            regex = new RegExp(regexString);

        var isValid = regex.test(args.Value);

        v._toggleInputErrorState(input, errorClass, isValid);
        args.IsValid = isValid;
        return;
    },    

    /************* Helper functions ***********/

    _toggleInputErrorState: function (inputEl, errorClass, isValid) {
        if (!isValid) {
            if (!v._hasClass(inputEl, errorClass)) {
                inputEl.className +="" + errorClass;
            }
        } else {
            if (v._hasClass(inputEl, errorClass)) {
                //Not the most performant, but is sure is easiest
                inputEl.className = inputEl.className.replace("" + errorClass,"");
            }
        }
    },

    _hasClass: function(el, className) {
        return el.className.indexOf(className) != -1 ? true : false;
    },
}

非常简单的验证库,您可以轻松地扩展您真正感兴趣的东西。

默认.aspx

之后,您可以将控件放入您的页面:

1
2
<Validators:RequiredFieldValidator runat="server" CssControlErrorClass="input-validation-error" ControlToValidate="Test" ErrorMessage="REQUIRED BRO!"></Validators:RequiredFieldValidator>
<Validators:RegularExpressionValidator runat="server" ValidationExpression="[0-9]" CssControlErrorClass="input-validation-error" ControlToValidate="Test" ErrorMessage="REQUIRED RegEx BRO!"></Validators:RegularExpressionValidator>

这是最好的方法吗?可能不是,(这两个使用微软提供的默认实现)那里有比我更聪明的人,而且我不经常使用 WebForms。我看到的最大好处是您获得了一些可重用的代码,使用熟悉的语法,最终将包含您所有的验证需求,而不是每次都使用 js 来获得您想要的验证"规则"。


您可以在 javascript 中尝试 Page_ClientValidate(),而不是遍历验证器。
我相信这将验证页面上的所有验证器。
如果要验证受特定验证组绑定的特定控件,它还接受参数"验证组名称"。