Здесь есть 2 вопроса, оба связанных.
Первая проблема связана с использованием [Required] bool?
и jQuery Validate в .NET MVC, где не запрещается отправка формы, где нет значения, и не отображается предупреждающее сообщение, когда пользователь изменяет переключатель на средняя null
позиция (истина / ноль / ложь).
Вторая проблема связана с использованием [BooleanRequired] bool
и использованием CheckBoxFor с и jQuery Validate в .NET MVC, при этом в определенных условиях CheckBox фактически исчезает. Например, в Chrome, если вы публикуете форму, не отмечая флажок Условия, состояние :: before удаляется, и поэтому CheckBox исчезает, чего не происходит в IE11, однако снятие галочки с причин CheckBox после отображения предупреждения заставляет CheckBox исчезают.
Ниже приведен код для обеих проблем:
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.
Я надеюсь, что кто-то может пролить свет на то, что я могу делать неправильно или как обойти эти проблемы.