Asp .net mvc 3 Метод CheckBoxFor выводит скрытое поле, это значение скрытого поля равно false, когда флажок отключен с выбранным значением true
CheckBoxFor(t => t.boolValue, новый { disabled="disabled" }) метод для отображения флажка в отключенном режиме.
Метод также отображает скрытое поле.
У меня вопрос, почему это скрытое поле имеет ложное значение для отключенного флажка? Я считаю, что цель скрытого поля состоит в том, чтобы иметь некоторое дополнительное поведение по сравнению с поведением флажка по умолчанию
Есть ли способ переопределить функциональность MVC по умолчанию, чтобы значение этого скрытого поля основывалось на состоянии флажка даже в отключенном режиме?
4 ответа
Скрытое поле используется для привязки значения флажка к логическому свойству. Дело в том, что если флажок не установлен, на сервер ничего не отправляется, поэтому ASP.NET MVC использует это скрытое поле для отправки false и привязки к соответствующему логическому полю. Вы не можете изменить это поведение, кроме написания специального помощника.
Это, как говорится, вместо того, чтобы использовать disabled="disabled"
использование 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)
Вот небольшой совет, который я обнаружил: если вы установите значение по умолчанию для логического значения объекта. Можно записать тег со значением true / false (это значение должно быть противоположным логическому значению по умолчанию для объекта)
<input type="checkbox" name="IsAllDayEvent" value="true" id="IsAllDayEvent" />
Значение отправляется только если флажок установлен, поэтому он работает.
Хотя ответ Дарина в порядке, есть еще один способ убедиться, что отключенный флажок отправляет сообщения должным образом.
Это решение также будет работать в тех случаях, когда флажок условно отключен на основе свойства в модели или включен / отключен на стороне клиента на основе выбора пользователя или полученных на стороне клиента данных.
Вы можете сделать ваше представление простым: (отключение здесь необязательно)
@Html.CheckBoxFor(m => m.Something, new { id = "somethingCheckbox", disabled = "disabled" })
Это, как упомянул Дарин, сгенерирует дополнительное скрытое поле ввода, установленное в 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)
Возможно, вам придется изменить его для вашего сценария, но вы должны понять.