I have written a helper which returns html attributes as a dictionary, which is then used by any helper to render the attributes.
The attributes are then used by some javascript code.
The code is not important, but here it is
namespace MvcHtmlHelpers
{
public class PropertyValuePairs<TModel>
{
public readonly IDictionary<string, object> Pairs = new Dictionary<string, object>();
private HtmlHelper<TModel> _html;
public PropertyValuePairs(HtmlHelper<TModel> html)
{
_html=html;
}
public PropertyValuePairs<TModel> AddNameFor<TValue>(Expression<Func<TModel, TValue>> expression, object value)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string name = _html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
return AddName(name, value);
}
public PropertyValuePairs<TModel> AddName(string name, object value)
{
Pairs.Add(name, value);
return this;
}
public PropertyValuePairs<TModel> AddIdFor<TValue>(Expression<Func<TModel, TValue>> expression, object value)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string id = _html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName);
return AddName(id, value);
}
public PropertyValuePairs<TModel> AddId(string id, object value)
{
Pairs.Add('#' + id, value);
return this;
}
}
public enum LogicalOperator { And, Or }
public static class EnabledIfAttributes
{
public static PropertyValuePairs<TModel> NewPropertyValues<TModel>(this HtmlHelper<TModel> html)
{
return new PropertyValuePairs<TModel>(html);
}
//for use in javaScript - lower case property names are intentional
public class PropertyPair
{
public string name { get; set; }
public object val { get; set; }
}
public class dataAttribute
{
public string logicalOperator { get; set; }
public List<PropertyPair> propertyPairs { get; set; }
}
public const string ClassName = "enabledif";
public static string AttributeName = "data-" + ClassName;
public static IDictionary<string, object> EnabledIfAttributesFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object value)
{
return EnabledIfAttributesFor(html, expression, value, new RouteValueDictionary());
}
public static IDictionary<string, object> EnabledIfAttributesFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object value, object htmlAttributes)
{
return EnabledIfAttributesFor(html, expression, value, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static IDictionary<string, object> EnabledIfAttributesFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object value, IDictionary<string, object> htmlAttributes)
{
var pairList = new PropertyValuePairs<TModel>(html).AddNameFor(expression, value);
return EnabledIfAttributesFor(html, pairList, new RouteValueDictionary(htmlAttributes));
}
public static IDictionary<string, object> EnabledIfAttributesFor<TModel>(this HtmlHelper<TModel> html, PropertyValuePairs<TModel> values, LogicalOperator logicalOperator = LogicalOperator.Or)
{
return EnabledIfAttributesFor(html, values, new RouteValueDictionary(), logicalOperator);
}
public static IDictionary<string, object> EnabledIfAttributesFor<TModel>(this HtmlHelper<TModel> html, PropertyValuePairs<TModel> values, object htmlAttributes, LogicalOperator logicalOperator = LogicalOperator.Or)
{
return EnabledIfAttributesFor(html, values, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes), logicalOperator);
}
public static IDictionary<string, object> EnabledIfAttributesFor<TModel>(this HtmlHelper<TModel> html, PropertyValuePairs<TModel> values, IDictionary<string, object> htmlAttributes, LogicalOperator logicalOperator = LogicalOperator.Or)
{
//in this case expression refers to other property
if (htmlAttributes.ContainsKey("class"))
{
string existingClass = htmlAttributes["class"].ToString().Trim();
htmlAttributes["class"] = existingClass + ' ' + ClassName;
}
else
{
htmlAttributes.Add("class",ClassName);
}
var attr = new dataAttribute
{
logicalOperator = logicalOperator.ToString(),
propertyPairs = new List<PropertyPair>()
};
foreach (var pair in values.Pairs)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(pair.Key);
attr.propertyPairs.Add(new PropertyPair
{
name = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName),
val = pair.Value
});
}
htmlAttributes.Add(AttributeName, JsonConvert.SerializeObject(attr));
return htmlAttributes;
}
}
}
it is used in code like so
@Html.CheckBoxFor(model => model.AllExclusionCriteriaAbsent, @Html.EnabledIfAttributesFor(model=>model.AllInclusionCriteriaPresent, true))
I would like to add the same attributes to a <div></div> ie there is no helper. Writing a div helper is silly, so I was wondering if there is a pre-existing method to render a dictionary as html attributes (there must be one used by all the helpers which come with MVC), or a way of cleanly/concisely doing this with razor 2. Thank you.
using Ross's suggestion, it seems there are 2 easier ways to approach this:
namespace MvcHtmlHelpers
{
public static class SimpleTagHelper
{
public static MvcHtmlString Element(string tagName, IDictionary<string, object> attributes, TagRenderMode renderMode=TagRenderMode.Normal)
{
var tag = new TagBuilder(tagName);
tag.MergeAttributes(attributes);
return MvcHtmlString.Create(tag.ToString(renderMode));
}
public static MvcHtmlString ToAttributes(this IDictionary<string, object> attributeDictionary)
{
return MvcHtmlString.Create(string.Join(" ",attributeDictionary.Select(d=>string.Format("{0}=\"{1}\"",d.Key,HttpUtility.HtmlAttributeEncode(d.Value.ToString())))));
}
}
}
and then choose the semantics you prefer:
@SimpleTagHelper.Element("div", Html.EnabledIfAttributesFor(model => model.AllExclusionCriteriaAbsent, true, new { @class="optionList"}), TagRenderMode.StartTag)
or
<div @Html.EnabledIfAttributesFor(model => model.AllExclusionCriteriaAbsent, true, new { @class="optionList"}).ToAttributes() >
MVC.Net uses the TagBuilder class (MSDN) internally in its Html helper methods.
E.g. here's some code for building a Label tag:
TagBuilder tag = new TagBuilder("label");
tag.SetInnerText(resolvedLabelText);
tag.MergeAttributes(htmlAttributes, replaceExisting: true);
return tag.ToMvcHtmlString(TagRenderMode.Normal);