可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an ASP.Net MVC 3 web application and I am adding a check box to a view page using the HtmlHelper class, like this...
@Html.CheckBox("CheckBox1", true, new { @class = "Class1" })
What I want to do is conditionally add the disabled attribute based on a view state property. Basically the following would be ideal...
@Html.CheckBox("CheckBox1", true, new { @class = "Class1", @disabled = Model.ReadOnly })
Unfortunately, due to the nature of the disabled attribute, this will not work because any value assigned to the disabled attribute (even "false") will be translated as true.
I have already thought of a few solutions to get round this problem, so the question is not how can I do this. But rather, is there a simple way like the desired method above? or do I have to resort to one of the following?..
What I know I could do...
Create an if/else statement and write to different Html.CheckBox
lines (not great for readability - and possible with throw a mark-up warning - not sure)
Skip the HtmlHelper class and hand write the tag allowing for better conditionally attributes (keeps the code shorter, but adds inconsistency)
Create a custom helper that takes a "disabled" parameter (the cleanest solution, but requires undesired extra methods - probably the best option so far though)
回答1:
Define this somewhere in your view/helpers
@functions {
object getHtmlAttributes (bool ReadOnly, string CssClass)
{
if (ReadOnly) {
return new { @class = CssClass, @readonly = "readonly" };
}
return new { @class = CssClass };
}
}
Then use :
@Html.TextBox("name", "value", @getHtmlAttributes(Model.ReadOnly, "test"))
回答2:
Here's my answer from this similar question: https://stackoverflow.com/a/13922813/495000
I created the following Helper - it takes a boolean and an anonymous object. If disabled is true, it adds the disabled attribute to the anonymous object (which is actually a Dictionary) with the value "disabled", otherwise it doesn't add the property at all.
public static RouteValueDictionary ConditionalDisable(
bool disabled,
object htmlAttributes = null)
{
var dictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (disabled)
dictionary.Add("disabled", "disabled");
return dictionary;
}
An example of it in action:
@Html.TextBoxFor(m => m.SomeProperty,
HtmlHelpers.ConditionalDisable(true, new { @class = "someClass"))
One huge advantage to this approach for me was that it works with virtually all of the MVC HtmlHelpers since they all have Overloads that accept a RouteValueDictionary instead of an anonymous object.
Caveats:
HtmlHelper.AnonymousObjectToHtmlAttributes()
uses some fancy code ninja work to get things done. I'm not entirely sure how performant it is... but it's been sufficient for what I use it for. Your mileage may vary.
I don't especially like the name of it - but I couldn't come up with anything better. Renaming is easy.
I also don't love the usage syntax - but again I couldn't come up with anything better. It shouldn't be difficult to change. An extension method on object is one idea... you'd end up with new { @class = "someClass" }.ConditionalDisable(true)
but then if you only want the disable attribute and don't have anything additional to add you end up with something gross like new {}.ConditionalDisable(true);
and you also end up with an extension method that shows up for all object
... which is probably not desirable.
回答3:
If you want more terse syntax without requiring a helper function, you could use a ternary statement when defining the dictionary used for the html attributes of the @HTML.Checkbox helper...
@Html.CheckBox("CheckBox1", true, Model.ReadOnly
? new { @class = "Class1", @disabled = Model.ReadOnly }
: null)
In this case is Model.ReadOnly is false, null gets passed as the dictionary of html attributes.
回答4:
@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.
/// e.g. 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 e.g. 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;
}
}
回答5:
What do you think about my simple solution? It works easily with both possible HtmlAttributes
types:
Dictionary<string, object>
Anonymous Object
:
First add the following simple extension class
to your project:
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;
}
}
Now in View:
Example1 (HtmlAttributes
type as Anonymous Object
)
@{
var hasDisabled=true;
}
@Html.CheckBox("CheckBox1"
, true
, new { @class = "Class1"}
.AddHtmlAttrItem("disabled", "disabled", hasDisabled))
.
Example 2 (HtmlAttributes
type as Dictionary<string, object>
)
@Html.CheckBox("CheckBox1"
, true
, new Dictionary<string, object> { { "class", "Class1" }
.AddHtmlAttrItem("disabled", "disabled", hasDisabled))
.
Now just change the hasDisabled
value to true
or false
!
Example3 (Multiple conditional properties)
@{
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))
.
回答6:
Performing the addition of the disabled attribute client side works for me. Note you should check which fields are allowed to be edited server side, but that is true for where the disabled attribute is declared decoratively also.
In this example I have disabled all childeren of a form using jQuery.
if (Model.CanEdit)
{
<script type="text/javascript">
$(document).ready(function() {
$('#editForm *').attr('disabled', true);
});
</script>
}