C# 布尔和布尔? jQuery 验证问题

Posted

技术标签:

【中文标题】C# 布尔和布尔? jQuery 验证问题【英文标题】:C# bool and bool? jQuery Validate issues 【发布时间】:2019-09-14 16:29:50 【问题描述】:

这里有两个问题,都是相关的。

第一个问题与在 .NET MVC 中使用 [Required] bool? 和 jQuery Validate 相关,其中不会阻止表单在不存在值的情况下发布,也不会在用户修改切换到中间null 位置(真/空/假)。

第二个问题与在 .NET MVC 中使用 [BooleanRequired] bool 和使用 CheckBoxFor 和 jQuery Validate 相关,因为在某些情况下 CheckBox 实际上会消失。例如,在 Chrome 中,如果您在未勾选条款复选框的情况下发布表单,则 ::before 状态将被删除,因此复选框消失,这在 IE11 中不会发生,但是在显示警告后取消勾选复选框确实会导致复选框消失。

以下两个问题的代码如下:

HomeController.cs

public ActionResult Test()

    return View();


[HttpPost]
public ActionResult Test(Test test)

    if (ModelState.IsValid)
    
        //return Redirect("/thank-you");
    

    return View(test);

Test.cshtml

<section class="content container">
    <article class="content content__full">
        @html.Partial("_TestForm")
    </article>
</section>

_TestForm.cshtml

@using WebAppliation1.Helpers
@model WebAppliation1.Models.Test

@using (Html.BeginForm("Test", "Home", FormMethod.Post, new  id = "TestForm", @class = "test-form" ))

    @Html.ValidationSummary()
    <div id="QuestionDiv">
        <label>Are you a badger?</label>
        @Html.HiddenForExt(model => model.Question, new Dictionary<string, object>   "required", "required"  )
    </div>

    <footer>
        @Html.CheckBoxFor(model => model.Terms, new Dictionary<string, object>   "required", "required"  )
        @Html.LabelFor(model => model.Terms)
        <a href="/terms-and-conditions" target='_Blank'>Terms and Conditions</a>
        @Html.ValidationMessageFor(model => model.Terms)
        <input type="submit" value="Submit" class="btn" />
    </footer>

@Scripts.Render("~/Scripts/jqueryNouisliderAll")
@Scripts.Render("~/Scripts/test")

test.js

$(document).ready(function () 
    var question = $("#Question").val();

    question = question === "" ? "1" : question === "True" ? "0" : "2";

    $("#QuestionDiv").toggle(
        selectorName: "QuestionDiv",
        displaySelectedValue: false,
        start: question
    );

    $("#QuestionDiv").on("change", function () 
        question = this.value;

        question = question === "1" ? null : question === "0" ? "True" : "False";

        $("#Question").val(question);
    );
);


$.validator.messages.required = function (param, input) 
    return $(input).data("val-required");


$.validator.addMethod("notEqual", function (value, element, param) 
    return this.optional(element) || value !== param;
, "Please specify a different (non-default) value");

$("#TestForm").validate(
    rules: 
        Question: 
            notEqual: null
        ,
        //Question: 
        //    //minlength: 4,
        //    //required: true
        //    required: function(element) //        
        //        return $("#QuestionDiv").value !== 1;
        //    
        //,
        Terms: 
            required: true
        
    ,
    messages: 
        Terms: 
            required: "You must accept the terms and conditions"
        ,
        Question: 
            minlength: "Please select either Yes or No",
            required: "The Question field is required"
        
    ,
    errorPlacement: function (error, element) 
        var text = error.text();

        if (text.indexOf("required") !== -1) 
            element.attr("placeholder", error.text());
         else 
            error.insertAfter(element);
        
    ,
    highlight: function (element) 
        if ($(element).is("select")) 
            $(element.form).find("select[id=" + element.id + "]").parent().find("span").addClass("error").removeClass("valid");
         else 
            $(element).addClass("error").removeClass("valid");
        
    ,
    unhighlight: function (element) 
        if ($(element).is("select")) 
            $(element.form).find("select[id=" + element.id + "]").parent().find("span").addClass("valid").removeClass("error");
         else 
            $(element).addClass("valid").removeClass("error");

            var label = $("label[for='" + element.id + "']").text();

            $(element).prop("placeholder", label);
        
    ,
    focusInvalid: true
);

(function ($) 
    var pluginName = "toggle";

    function plugin(element, options) 
        var toggle = null;

        var leftLabel = null;

        var rightLabel = null;

        var $el = $(element);

        var $toggle = null;

        var $leftLabel = null;

        var $rightLabel = null;

        options = $.extend(, $.fn[pluginName].defaults, options);

        function render() 
            $el.append("<span class='toggle-slider__option toggle-slider__option-left'>" + options.leftLabelText + "</span>");

            $el.append("<div class='toggle-slider'></div>");

            $el.append("<span class='toggle-slider__option toggle-slider__option-right'>" + options.rightLabelText + "</span>");

            var type = "hidden";

            if (options.displaySelectedValue) 
                type = "text";
            

            $el.append("<input id='" + options.selectorName + "' name='" + options.selectorName + "' class='toggle-slider-display' type='" + type + "' value='" + options.start + "'></input>");

            toggle = $el.children(".toggle-slider");

            leftLabel = $el.children(".toggle-slider__option-left");

            rightLabel = $el.children(".toggle-slider__option-right");

            $toggle = $(toggle);

            $leftLabel = $(leftLabel);

            $rightLabel = $(rightLabel);
        

        function configure() 
            $toggle.noUiSlider(
                range: 
                    'min': options.minVal,
                    'max': options.maxVal
                ,
                format: options.format,
                start: options.start
            );
        

        function toggleVal(value) 
            $("#" + options.selectorName).val(value);

            $leftLabel.removeClass("left");

            $rightLabel.removeClass("right");

            $toggle.removeClass("left");

            $toggle.removeClass("right");

            $toggle.removeClass("off");

            switch (value) 
                case "0":
                    $leftLabel.addClass("left");
                    $toggle.addClass("left");
                    break;
                case "2":
                    $rightLabel.addClass("right");
                    $toggle.addClass("right");
                    break;
                default:
                    $toggle.addClass("off");
            
        

        function bind() 
            $leftLabel.click(function () 
                $toggle.val(0);
            );

            $rightLabel.click(function () 
                $toggle.val(2);
            );

            $toggle.Link().to(toggleVal);
        

        function init() 
            render();
            configure();
            bind();
        

        function destroy() 
            $el.each(function () 
                var $el = $(this);

                hook("onDestroy");

                $el.removeData("plugin_" + pluginName);
            );
        

        init();

        return 
            destroy: destroy
        ;
    

    $.fn[pluginName] = function (options) 
        if (typeof options === "object" || !options) 
            return this.each(function () 
                if (!$.data(this, "plugin_" + pluginName)) 
                    $.data(this, "plugin_" + pluginName, new plugin(this, options));
                
            );
        
    ;

    $.fn[pluginName].defaults = 
        onInit: function ()  ,
        onDestroy: function ()  ,
        step: 1,
        minVal: [0, 1, 2],
        maxVal: 2,
        displaySelectedValue: true,
        start: 1,
        selectorName: pluginName + "Selector",
        format: wNumb(
            decimals: 0
        ),
        leftLabelText: "Yes",
        rightLabelText: "No"
    ;
($));

InputExtender.cs

public static MvcHtmlString HiddenForExt<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes = null, bool readOnly = false)

    if (htmlAttributes == null)
    
        htmlAttributes = new Dictionary<string, object>();
    

    var modelMetadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    if (modelMetadata != null) htmlAttributes.Add("id", modelMetadata.PropertyName);

    var memberExpression = expression.Body as MemberExpression;

    var stringLengthAttribute = memberExpression?.Member.GetCustomAttributes(typeof(StringLengthAttribute), false).FirstOrDefault() as StringLengthAttribute;

    if (stringLengthAttribute != null)
    
        if (htmlAttributes.ContainsKey("maxlength") == false)
        
            htmlAttributes.Add("maxlength", stringLengthAttribute.MaximumLength);
        
    

    return html.HiddenFor(expression, htmlAttributes);

Test.cs

public class Test

    [Required(ErrorMessage = "Are you a badger? Requires a Yes or No")]
    [Display(Name = "Are you a badger?")]
    public bool? Question  get; set; 

    [BooleanRequired(ErrorMessage = "You must accept the terms and conditions.")]
    [Display(Name = "I agree with the ")]
    public bool Terms  get; set; 

如果有帮助,我正在使用 jquery-2.1.4.js、jquery.validate-1.17.0.js 和 jquery.validate.unobtrusive-3.2.11.js。

我希望有人能说明我可能做错了什么或如何为这些问题提供解决方法。

【问题讨论】:

替换 errorPlacement 代码可解决 CheckBox errorPlacement: function (error, element) var text = error.text(); if (text.indexOf("required") !== -1) element.attr("placeholder", error.text()); else if ($(element).is(':checkbox')) error.insertAfter(element.closest("footer")); else error.insertAfter(element); 的可见性丢失问题,因此这是解决了一个问题。 由于您使用的是Unobtrusive Validation插件,因此您不能自己致电.validate()。 Unobtrusive 插件自动构造并调用.validate() 方法。而且由于插件不允许在同一个表单上多次调用.validate(),因此您的实例将始终被忽略。如果您可以以某种方式强制使用您的实例,那么 Unobtrusive 插件将完全没有意义。 【参考方案1】:

隐藏字段未被验证的问题是因为默认情况下它们被忽略,您必须指定是否要验证要使用的隐藏字段。要验证所有隐藏字段使用ignore: "",在我的例子中我使用ignore: ":hidden:not(#Question)"。之前使用 errorPlacement 导致 CheckBox 消失。

以下是修改后的test.js,它纠正了上述所有问题:

$(document).ready(function () 
    var question = $("#Question").val();

    question = question === "" ? "1" : question === "True" ? "0" : "2";

    $("#QuestionDiv").toggle(
        selectorName: "QuestionDiv",
        displaySelectedValue: false,
        start: question
    );

    $("#QuestionDiv").on("change", function () 
        question = this.value;

        if (question === "1") 
            $("label[for*='Question']").html("Are you a badger? requires either Yes or No");
         else 
            $("label[for*='Question']").html("Are you a badger?");
        

        question = question === "1" ? null : question === "0" ? "True" : "False";

        $("#Question").val(question);
    );
);

$.validator.messages.required = function (param, input) 
    return $(input).data("val-required");


$("#TestForm").validate(
    ignore: ":hidden:not(#Question)",
    rules: 
        Question: 
            required: function () 
                var questionDiv = $("#QuestionDiv").val();

                if (questionDiv === "1") 
                    $("label[for*='Question']").html("Are you a badger? requires either Yes or No");
                 else 
                    $("label[for*='Question']").html("Are you a badger?");
                

                return questionDiv === "1" ? true : false;
            
        ,
        Terms: 
            required: true
        
    ,
    messages: 
        Terms: 
            required: "You must accept the terms and conditions"
        ,
        Question: 
            required: "The Question field is required"
        
    ,
    errorPlacement: function (error, element) 
        var text = error.text();

        if (text.indexOf("required") !== -1) 
            element.attr("placeholder", error.text());
         else 
            if ($(element).is(':checkbox')) 
                error.insertAfter(element.closest("footer"));
             else 
                error.insertAfter(element);
            
        
    ,
    highlight: function (element) 
        if ($(element).is("select")) 
            $(element.form).find("select[id=" + element.id + "]").parent().find("span").addClass("error").removeClass("valid");
         else 
            $(element).addClass("error").removeClass("valid");
        
    ,
    unhighlight: function (element) 
        if ($(element).is("select")) 
            $(element.form).find("select[id=" + element.id + "]").parent().find("span").addClass("valid").removeClass("error");
         else 
            $(element).addClass("valid").removeClass("error");

            var label = $("label[for='" + element.id + "']").text();

            $(element).prop("placeholder", label);
        
    ,
    focusInvalid: false
);

(function ($) 
    var pluginName = "toggle";

    function plugin(element, options) 
        var toggle = null;

        var leftLabel = null;

        var rightLabel = null;

        var $el = $(element);

        var $toggle = null;

        var $leftLabel = null;

        var $rightLabel = null;

        options = $.extend(, $.fn[pluginName].defaults, options);

        function render() 
            $el.append("<span class='toggle-slider__option toggle-slider__option-left'>" + options.leftLabelText + "</span>");

            $el.append("<div class='toggle-slider'></div>");

            $el.append("<span class='toggle-slider__option toggle-slider__option-right'>" + options.rightLabelText + "</span>");

            var type = "hidden";

            if (options.displaySelectedValue) 
                type = "text";
            

            $el.append("<input id='" + options.selectorName + "' name='" + options.selectorName + "' class='toggle-slider-display' type='" + type + "' value='" + options.start + "'></input>");

            toggle = $el.children(".toggle-slider");

            leftLabel = $el.children(".toggle-slider__option-left");

            rightLabel = $el.children(".toggle-slider__option-right");

            $toggle = $(toggle);

            $leftLabel = $(leftLabel);

            $rightLabel = $(rightLabel);
        

        function configure() 
            $toggle.noUiSlider(
                range: 
                    'min': options.minVal,
                    'max': options.maxVal
                ,
                format: options.format,
                start: options.start
            );
        

        function toggleVal(value) 
            $("#" + options.selectorName).val(value);

            $leftLabel.removeClass("left");

            $rightLabel.removeClass("right");

            $toggle.removeClass("left");

            $toggle.removeClass("right");

            $toggle.removeClass("off");

            switch (value) 
                case "0":
                    $leftLabel.addClass("left");
                    $toggle.addClass("left");
                    break;
                case "2":
                    $rightLabel.addClass("right");
                    $toggle.addClass("right");
                    break;
                default:
                    $toggle.addClass("off");
            
        

        function bind() 
            $leftLabel.click(function () 
                $toggle.val(0);
            );

            $rightLabel.click(function () 
                $toggle.val(2);
            );

            $toggle.Link().to(toggleVal);
        

        function init() 
            render();
            configure();
            bind();
        

        function destroy() 
            $el.each(function () 
                var $el = $(this);

                hook("onDestroy");

                $el.removeData("plugin_" + pluginName);
            );
        

        init();

        return 
            destroy: destroy
        ;
    

    $.fn[pluginName] = function (options) 
        if (typeof options === "object" || !options) 
            return this.each(function () 
                if (!$.data(this, "plugin_" + pluginName)) 
                    $.data(this, "plugin_" + pluginName, new plugin(this, options));
                
            );
        
    ;

    $.fn[pluginName].defaults = 
        onInit: function ()  ,
        onDestroy: function ()  ,
        step: 1,
        minVal: [0, 1, 2],
        maxVal: 2,
        displaySelectedValue: true,
        start: 1,
        selectorName: pluginName + "Selector",
        format: wNumb(
            decimals: 0
        ),
        leftLabelText: "Yes",
        rightLabelText: "No"
    ;
($));

我还将 &lt;label&gt;Are you a badger?&lt;/label&gt; 修改为 @Html.LabelFor(model =&gt; model.Question) 以正确定位 jQuery $("label[for*='Question']") 中的标签,并在使用表单验证或用户交互时使用附加代码来控制标签文本,因为即使 @Html.ValidationMessageFor(model =&gt; model.Question) 也会被隐藏,因为使用@Html.HiddenForExt(model =&gt; model.Question, new Dictionary&lt;string, object&gt; "required", "required" )

【讨论】:

以上是关于C# 布尔和布尔? jQuery 验证问题的主要内容,如果未能解决你的问题,请参考以下文章

C#如何 将一个布尔型数组全部 填充为false

c#中布尔值和布尔值的区别? [复制]

C#中的原始布尔大小

为啥是布尔值和布尔值

正经学C#_布尔运算[布尔值与其布尔运算符]:《c#入门经典》

为啥 Java 和 C# 没有到布尔值的隐式转换?