MVC 3:使用HtmlHelpers有条件地添加已禁用的属性

我有一个ASP.Net MVC 3的Web应用程序,我正在添加一个checkbox使用HtmlHelper类的视图页面,像这样…

@Html.CheckBox("CheckBox1", true, new { @class = "Class1" }) 

我想要做的是有条件地添加基于视图状态属性的禁用属性。 基本上下面是理想的…

 @Html.CheckBox("CheckBox1", true, new { @class = "Class1", @disabled = Model.ReadOnly }) 

不幸的是,由于禁用属性的性质,这将无法正常工作,因为任何分配给被禁用属性的值(甚至“false”)都将被翻译为true。

我已经想到了解决这个问题的一些解决scheme,所以问题不是我怎么能做到这一点。 但是,有没有像上面所需的方法一个简单的方法? 还是我不得不诉诸以下之一?

我知道我可以做…

  1. 创build一个if / else语句并写入不同的Html.CheckBox行(不是很好的可读性 – 可能抛出一个标记警告 – 不知道)

  2. 跳过HtmlHelper类并手写标签,以允许更好的条件属性(保持代码更短,但添加不一致)

  3. 创build一个自定义的助手,它需要一个“禁用”参数(最干净的解决scheme,但需要不必要的额外方法 – 尽pipe目前为止可能是最好的select)

在你的视图/助手中定义这个地方

 @functions { object getHtmlAttributes (bool ReadOnly, string CssClass) { if (ReadOnly) { return new { @class = CssClass, @readonly = "readonly" }; } return new { @class = CssClass }; } } 

然后使用:

 @Html.TextBox("name", "value", @getHtmlAttributes(Model.ReadOnly, "test")) 

这是我从这个类似的问题的答案: https : //stackoverflow.com/a/13922813/495000


我创build了以下帮助器 – 它需要一个布尔值和一个匿名对象。 如果禁用为true,则将disabled属性添加到匿名对象(实际上是Dictionary),值为“disabled”,否则不会添加属性。

 public static RouteValueDictionary ConditionalDisable( bool disabled, object htmlAttributes = null) { var dictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); if (disabled) dictionary.Add("disabled", "disabled"); return dictionary; } 

它的一个例子是:

 @Html.TextBoxFor(m => m.SomeProperty, HtmlHelpers.ConditionalDisable(true, new { @class = "someClass")) 

对我来说,这种方法的一个巨大的优势是,它几乎可以与所有的MVC HtmlHelpers一起工作,因为它们都具有接受RouteValueDictionary而不是匿名对象的Overload。

注意事项
HtmlHelper.AnonymousObjectToHtmlAttributes()使用一些奇特的代码忍者工作来完成任务。 我不完全确定它的性能如何……但是对于我使用它的已经足够了。 你的旅费可能会改变。

我不是特别喜欢它的名字 – 但是我不能拿出更好的东西。 重命名很简单。

我也不喜欢使用的语法 – 但我再也找不到更好的东西了。 改变应该不难。 对象的扩展方法是一个想法…你最终会得到new { @class = "someClass" }.ConditionalDisable(true)但是如果你只想禁用属性,并没有任何额外的东西来添加你最终得到的结果就像new {}.ConditionalDisable(true); 而且你最终还得到一个扩展方法,它显示所有的object …这可能是不可取的。

如果你想要更简洁的语法而不需要帮助函数,那么在定义用于@ HTML.Checkbox助手的html属性的字典时,可以使用三元语句。

 @Html.CheckBox("CheckBox1", true, Model.ReadOnly ? new { @class = "Class1", @disabled = Model.ReadOnly } : null) 

在这种情况下,Model.ReadOnly为false,null作为html属性的字典传递。

执行添加禁用的属性客户端工作适合我。 请注意,您应该检查哪些字段被允许在服务器端进行编辑,但是对于禁用的属性的装饰位置也是如此。

在这个例子中,我使用jQuery禁用了一个窗体的所有childeren。

  if (Model.CanEdit) { <script type="text/javascript"> $(document).ready(function() { $('#editForm *').attr('disabled', true); }); </script> } 
 @Html.TextBoxFor(m => m.FieldName, Html.FixBoolAttributes(new { @class = "myClass", @readonly = myFlag })) public static class BooleanAttributeFix { /// <summary> /// Normalises HTML boolean attributes so that readonly=true becomes readonly="readonly" and /// readonly=false removes the attribute completely. /// </summary> /// <param name="htmlHelper"></param> /// <param name="htmlAttributes"></param> /// <returns></returns> public static RouteValueDictionary FixBoolAttributes(this HtmlHelper htmlHelper, object htmlAttributes) { var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); foreach(var attrName in new[] { "disabled", "readonly" }) { object value; if(attrs.TryGetValue(attrName, out value)) { if(isTruthy(value)) { // Change from readonly="true" to readonly="readonly" attrs[attrName] = attrName; } else { // Remove attribute entirely attrs.Remove(attrName); } } } return attrs; } /// <summary> /// Apply similar loose rules like javascript does for whether a value is true or not. /// eg 1 = true, non-empty string = true and so on. /// </summary> /// <param name="val"></param> /// <returns></returns> private static bool isTruthy(object val) { if(val == null) return false; if(val is string) { return !String.IsNullOrEmpty((string)val); } Type t = val.GetType(); if(t.IsValueType && Nullable.GetUnderlyingType(t) == null) { // If a non-nullable value type such as int we want to check for the // default value eg 0. object defaultValue = Activator.CreateInstance(t); // Use .Equals to compare the values rather than == as we want to compare // the values rather than the boxing objects. // See http://stackoverflow.com/questions/6205029/comparing-boxed-value-types return !val.Equals(defaultValue); } return true; } } 

你怎么看待我的简单解决scheme? 它可以轻松地使用两种可能的HtmlAttributestypes:

  • Dictionary<string, object>
  • Anonymous Object

首先将以下简单的extension class添加到您的项目中:

 public static class HtmlAttributesExtensions { public static IDictionary<string, object> AddHtmlAttrItem(this object obj, string name, object value, bool condition) { var items= !condition ? new RouteValueDictionary(obj) : new RouteValueDictionary(obj) {{name, value}}; return UnderlineToDashInDictionaryKeys(items); } public static IDictionary<string, object> AddHtmlAttrItem(this IDictionary<string, object> dictSource, string name, object value, bool condition) { if (!condition) return dictSource; dictSource.Add(name, value); return UnderlineToDashInDictionaryKeys(dictSource); } private static IDictionary<string, object> UnderlineToDashInDictionaryKeys(IDictionary<string,object> items) { var newItems = new RouteValueDictionary(); foreach (var item in items) { newItems.Add(item.Key.Replace("_", "-"), item.Value); } return newItems; } } 

现在在视图中:

示例1 HtmlAttributestypes为Anonymous Object

 @{ var hasDisabled=true; } @Html.CheckBox("CheckBox1" , true , new { @class = "Class1"} .AddHtmlAttrItem("disabled", "disabled", hasDisabled)) . 

示例2 HtmlAttributestypes为Dictionary<string, object>

 @Html.CheckBox("CheckBox1" , true , new Dictionary<string, object> { { "class", "Class1" } .AddHtmlAttrItem("disabled", "disabled", hasDisabled)) . 

现在只需将makeItReadOnly值更改为false


示例3 (多个条件属性)

 @{ var hasDisabled=true; var hasMax=false ; var hasMin=true ; } @Html.CheckBox("CheckBox1" , true , new { @class = "Class1"} .AddHtmlAttrItem("disabled", "disabled", hasDisabled) .AddHtmlAttrItem("data-max", "100", hasMax) .AddHtmlAttrItem("data-min", "50", hasMin)) .