CheckBoxFor(t => t.boolValue, new { disabled="disabled" }) 方法以在禁用模式下呈现复选框。
该方法也呈现一个隐藏字段。
我的问题是为什么这个隐藏字段的禁用复选框的值是错误的?我相信隐藏字段的目的是对默认复选框行为进行一些额外的行为
有没有办法覆盖默认的 MVC 功能,以便即使在禁用模式下,这个隐藏字段的值也基于复选框的状态?
CheckBoxFor(t => t.boolValue, new { disabled="disabled" }) 方法以在禁用模式下呈现复选框。
该方法也呈现一个隐藏字段。
我的问题是为什么这个隐藏字段的禁用复选框的值是错误的?我相信隐藏字段的目的是对默认复选框行为进行一些额外的行为
有没有办法覆盖默认的 MVC 功能,以便即使在禁用模式下,这个隐藏字段的值也基于复选框的状态?
隐藏字段用于将复选框值绑定到布尔属性。问题是,如果未选中复选框,则不会向服务器发送任何内容,因此 ASP.NET MVC 使用此隐藏字段发送 false 并绑定到相应的布尔字段。除了编写自定义帮助程序之外,您无法修改此行为。
话虽这么说,而不是在复选框上使用disabled="disabled"
use 。readonly="readonly"
这样,您将保持相同的期望行为,即用户无法修改其值,但除此之外,它的值将在提交表单时发送到服务器:
@Html.CheckBoxFor(x => x.Something, new { @readonly = "readonly" })
更新:
正如评论部分所指出的,该readonly
属性不适用于 Google Chrome。另一种可能性是使用另一个隐藏字段并禁用复选框:
@Html.HiddenFor(x => x.Something)
@Html.CheckBoxFor(x => x.Something, new { disabled = "disabled" })
更新 2:
这是一个带有附加隐藏字段的完整测试用例。
模型:
public class MyViewModel
{
public bool Foo { get; set; }
}
控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel { Foo = true });
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content(model.Foo.ToString());
}
}
看法:
@model MyViewModel
@using (Html.BeginForm())
{
@Html.HiddenFor(x => x.Foo)
@Html.CheckBoxFor(x => x.Foo, new { disabled = "disabled" })
<button type="submit">OK</button>
}
提交表单时, Foo 属性的值为true
。我已经测试了所有主流浏览器(Chrome、FF、IE)。
我发现查询字符串中添加的参数看起来很乱,让人们认为我们作为开发人员做错了什么。所以我采用了创建自己的 InputExtensions 类的极端方法,它允许我决定是否要呈现隐藏的输入。
以下是我创建的 InputExtensions 类(基于现有的 MVC 代码以保持完全兼容):
public static class InputExtensions
{
//
// Checkboxes
//
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression);
}
return CheckBoxFor(htmlHelper, expression, false);
}
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, object htmlAttributes, bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression, htmlAttributes);
}
return CheckBoxFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes), false);
}
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, IDictionary<string, object> htmlAttributes,
bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression, htmlAttributes);
}
if (expression == null)
{
throw new ArgumentNullException("expression");
}
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
bool? isChecked = null;
if (metadata.Model != null)
{
bool modelChecked;
if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
{
isChecked = modelChecked;
}
}
return CheckBoxHelper(htmlHelper, metadata, ExpressionHelper.GetExpressionText(expression), isChecked, htmlAttributes);
}
private static MvcHtmlString CheckBoxHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string name, bool? isChecked, IDictionary<string, object> htmlAttributes)
{
RouteValueDictionary attributes = ToRouteValueDictionary(htmlAttributes);
bool explicitValue = isChecked.HasValue;
if (explicitValue)
{
attributes.Remove("checked"); // Explicit value must override dictionary
}
return InputHelper(htmlHelper,
InputType.CheckBox,
metadata,
name,
value: "true",
useViewData: !explicitValue,
isChecked: isChecked ?? false,
setId: true,
isExplicitValue: false,
format: null,
htmlAttributes: attributes);
}
//
// Helper methods
//
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (string.IsNullOrEmpty(fullName))
{
throw new ArgumentException("Value cannot be null or empty.", "name");
}
var tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
var usedModelState = false;
bool? modelStateWasChecked = GetModelStateValue(htmlHelper.ViewData, fullName, typeof(bool)) as bool?;
if (modelStateWasChecked.HasValue)
{
isChecked = modelStateWasChecked.Value;
usedModelState = true;
}
if (!usedModelState)
{
string modelStateValue = GetModelStateValue(htmlHelper.ViewData, fullName, typeof(string)) as string;
if (modelStateValue != null)
{
isChecked = string.Equals(modelStateValue, valueParameter, StringComparison.Ordinal);
usedModelState = true;
}
}
if (!usedModelState && useViewData)
{
isChecked = EvalBoolean(htmlHelper.ViewData, fullName);
}
if (isChecked)
{
tagBuilder.MergeAttribute("checked", "checked");
}
tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
if (setId)
{
tagBuilder.GenerateId(fullName);
}
// If there are any errors for a named field, we add the css attribute.
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing));
}
private static RouteValueDictionary ToRouteValueDictionary(IDictionary<string, object> dictionary)
{
return dictionary == null ? new RouteValueDictionary() : new RouteValueDictionary(dictionary);
}
private static object GetModelStateValue(ViewDataDictionary viewData, string key, Type destinationType)
{
ModelState modelState;
if (viewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, culture: null);
}
}
return null;
}
private static bool EvalBoolean(ViewDataDictionary viewData, string key)
{
return Convert.ToBoolean(viewData.Eval(key), CultureInfo.InvariantCulture);
}
}
然后你可以像这样调用该方法:
@Html.CheckBoxFor(t => t.boolValue, new { disabled="disabled" }, false)
这是我发现的一个快速提示:如果您为对象上的布尔值设置默认值。可以写出值为真/假的标签(此值应与对象上的默认布尔值相反)
<input type="checkbox" name="IsAllDayEvent" value="true" id="IsAllDayEvent" />
只有在选中复选框时才发送该值,因此它有效。
尽管达林的回答非常好,但还有另一种方法可以确保禁用的复选框按预期回发。
此解决方案也适用于基于模型中的属性有条件地禁用复选框,或基于用户选择或客户端检索数据启用/禁用客户端的情况。
您可以保持视图简单:(此处禁用设置是可选的)
@Html.CheckBoxFor(m => m.Something, new { id = "somethingCheckbox", disabled = "disabled" })
正如 Darin 所提到的,这将生成一个额外的隐藏输入字段,设置为 false,这样复选框值也会在未选中复选框时回发。因此,我们知道该Something
字段将始终回发,无论复选框是否选中,禁用与否。唯一的问题是,如果复选框被禁用,它将始终以False
.
我们可以在提交之前通过一些 JavaScript 后处理来解决这个问题。在我的情况下,代码看起来像这样:
var dataToPost = $form.serializeArray();
if (dataToPost != null) {
for (var i = 0; i < dataToPost.length; i++) {
if (dataToPost[i].name === "Something") {
dataToPost[i].value = $("#somethingCheckbox").attr("checked") === "checked";
}
}
}
$.post($form.attr("action"), dataToPost)
您可能需要针对您的场景对其进行修改,但您应该明白这一点。