Reputation: 11721
I am trying to make a HtmlHelper and I need to allow users to add their own custom attributes to the html tag.
I tried to do this using the TagBuilder class, but it seems that instead of merging the attributes, it just replaces them.
This is what I did in C#:
public static MvcHtmlString List(HtmlHelper helper, object htmlAttributes)
{
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
var tag = new TagBuilder("div");
tag.AddCssClass("myClass");
tag.MergeAttributes(attributes, false);
// tag class property has value "myClass", not "myClass testClass"
return new MvcHtmlString("<div>");
}
This is my view:
@Html.List(new { @class = "testClass" })
What am I doing wrong?
Upvotes: 12
Views: 10194
Reputation: 2756
I needed other attributes to be merged (besides just class), so AddCssClass() wasn't sufficient. I wrote an extension method to do what I thought MergeAttributes was supposed to do:
public static class TagBuilderExtensions
{
public static void TrueMergeAttributes(this TagBuilder tagBuilder, IDictionary<string, object> attributes)
{
foreach (var attribute in attributes)
{
string currentValue;
string newValue = attribute.Value.ToString();
if (tagBuilder.Attributes.TryGetValue(attribute.Key, out currentValue))
{
newValue = currentValue + " " + newValue;
}
tagBuilder.Attributes[attribute.Key] = newValue;
}
}
}
Upvotes: 5
Reputation:
The MergeAttributes overrides the attributes already on the tag, AddCssClass appends the name in the class value.
So just switch it around and it will work;
tag.MergeAttributes(attributes, false);
tag.AddCssClass("myClass");
AddCssClass will append to the class name(s) merged above it.
Upvotes: 33
Reputation: 24125
The TagBuilder.MergeAttributes
method doesn't work how you expect it to. This is the exact code of this method:
public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting)
{
if (attributes != null)
{
foreach (var entry in attributes)
{
string key = Convert.ToString(entry.Key, CultureInfo.InvariantCulture);
string value = Convert.ToString(entry.Value, CultureInfo.InvariantCulture);
MergeAttribute(key, value, replaceExisting);
}
}
}
public void MergeAttribute(string key, string value, bool replaceExisting)
{
if (String.IsNullOrEmpty(key))
{
throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "key");
}
if (replaceExisting || !Attributes.ContainsKey(key))
{
Attributes[key] = value;
}
}
As you can see it only adds new attributes to the collection (if replaceExisting
is set to true it also replaces the ones already in the collection). It doesn't perform and attributes values merging logic. If you want to merge values you need to do it by yourself:
public static MvcHtmlString List(this HtmlHelperhelper, object htmlAttributes)
{
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (attributes.ContainsKey("class"))
attributes["class"] = "myclass " + attributes["class"];
else
attributes.Add("class", "myClass");
var tag = new TagBuilder("div");
tag.MergeAttributes(attributes, false);
return new MvcHtmlString(tag.ToString(TagRenderMode.Normal));
}
Upvotes: 18