28

TagBuilder is a nice implementation for build HTML elements. But -some- HTML elements can have another elements (I called like children). I could not find any class from Mvc classes.

Question; Should I implement few classes (TagBuilderTree, and TagBuilderNode) which support nested tags or did I miss something?

tereško
  • 56,151
  • 24
  • 92
  • 147
Nuri YILMAZ
  • 4,183
  • 5
  • 33
  • 41

3 Answers3

46

You can build the child elements in separate TagBuilders and put their generated HTML in the parent TagBuilder.

Here's an example: A <select> with some <option>s (example de-fatted for terseness)

TagBuilder select = new TagBuilder("select");  

foreach (var language in languages) // never ye mind about languages
{
    TagBuilder option = new TagBuilder("option");
    option.MergeAttribute("value", language.ID.ToString());

    if (language.IsCurrent)
    {
        option.MergeAttribute("selected", "selected");
    }

    option.InnerHtml = language.Description;
    // And now, the money-code:
    select.InnerHtml += option.ToString();
}
MrBoJangles
  • 11,501
  • 16
  • 60
  • 78
SLaks
  • 800,742
  • 167
  • 1,811
  • 1,896
  • I prefer to implement TagBuilderTree and TagBuilderNode it will encapsulate nested tags for our projects but I try to find answer why Razor doesn't serve them? Because it is look like necesary for all Razor users – Nuri YILMAZ Feb 10 '11 at 15:28
  • You mean WebPages, not Razor. It's not necessary for all developers; you can use static helpers instead. – SLaks Feb 10 '11 at 15:29
  • you are right, I talk about System.Web.Mvc.TagBuilder class. It doesn't have directly related with Razor engine. All engines can use it.. I am wondering why we doesn't have TagBuilder as tree and should I use TagBuilder for nested html tags. Or should I implement my own TagBuilderTree and TagBuilderNode classes? – Nuri YILMAZ Feb 10 '11 at 19:18
  • By nesting tagbuilders you essentially ruin the performance of the internally used StringBuilder class right? – Michiel Cornille Jun 07 '12 at 13:52
  • @Mvision: Correct. If the tags might be lengthy, XElement would probably be faster. – SLaks Jun 07 '12 at 14:01
  • I added an example, hope that's ok. – MrBoJangles Feb 22 '13 at 23:48
  • @MrBoJangles: A `StringBuilder` would be more efficient. – SLaks Feb 24 '13 at 01:34
  • Good to know. This is an example from code that builds a single ` – MrBoJangles Feb 25 '13 at 15:44
6

OK, I decided to do a little test in my own code base.

I compared these two methods to create the same exact final HTML:

  1. Manually generating the html using a StringBuilder
  2. Using multiple TagBuilders and nesting the content

Manually generating the html using a StringBuilder:


    var sb = new StringBuilder();
    sb.AppendLine("<div class='control-group'>");
    sb.AppendFormat(" <label class='control-label' for='{0}_{1}'>{2}</label>", propObj.ModelType, propObj.ModelProperty, propObj.LabelCaption);
    sb.AppendLine("  <div class='controls'>");
    sb.AppendFormat("    <input id='{0}_{1}' name='{0}[{1}]' value='{2}' />", propObj.ModelType, propObj.ModelProperty, propObj.PropertyValue);
    sb.AppendLine("  </div>");
    sb.AppendLine("</div>");

    return new HtmlString(sb.ToString());

Using multiple TagBuilders and merging the content:


    TagBuilder controlGroup = new TagBuilder("div");
    controlGroup.AddCssClass("control-group");

    TagBuilder label = new TagBuilder("label");
    label.AddCssClass("control-label");
    label.InnerHtml = propObj.LabelCaption;

    TagBuilder controls = new TagBuilder("div"); 

    TagBuilder input = new TagBuilder("input");
    input.Attributes["id"] = propObj.ModelType + "_" + propObj.ModelProperty;
    input.Attributes["name"] = propObj.ModelType + "[" + propObj.ModelProperty + "]";
    input.Attributes["value"] = propObj.PropertyValue;

    controls.InnerHtml += input;

    controlGroup.InnerHtml += label;
    controlGroup.InnerHtml += controls;

    return new HtmlString(controlGroup.ToString());

To me, #1 is easier to read and much more concise, but I can appreciat the structure of #2 also.

MattSlay
  • 6,877
  • 4
  • 39
  • 52
  • #1 is also a possible avenue for script injection if you're not careful to sanitize, I would just like to add. – Bon Jul 10 '14 at 06:11
  • 2
    I think #1 is more error prone because it relies on correctly typing the HTML with the correct placement of closing tags, single quotes double quotes etc. Plus TagBuilder has more intellisense. For example when adding a CSS class, stringbuilder will not provide any intellisense for adding a class whereas TagBulder has the AddCSSClass method which has intellisense. Also I find #2 much more readable. All the placeholders in method #1 make things less clear. – Louise Eggleton Aug 18 '14 at 08:59
-1

The problem that I have with TagBuilder to create tags is that it looks very un-maintainable. On the other hand, StringBuilder's AppendFormat not only makes the code maintainable, but also run with good efficiency.

I've made a slight improvement to MattSlay 's #1 method. I used only one call to StringBuilder's AppendFormat method and used the C# string literal to define the format. As a result the format ends up looking exactly like the desired result and runs with efficiency.

var sb = new StringBuilder();
    sb.AppendFormat(
     @"<div class='control-group'>
          <label class='control-label' for='{0}_{1}'>{2}</label>
          <div class='controls'>
             <input id='{0}_{1}' name='{0}[{1}]' value='{3}' />
          </div>
      </div>",
      propObj.ModelType,
      propObj.ModelProperty,
      propObj.LabelCaption,
      propObj.PropertyValue);

    return new HtmlString(sb.ToString());

Hope this helps!

Robert
  • 1,617
  • 4
  • 32
  • 59
Salman Hasrat Khan
  • 1,859
  • 1
  • 18
  • 25
  • it was old question. what is your proposal change classes as 'control-label'. all are hard coded as clasic asp (not asp.net) pages. i always think that maintenence is more important than code. – Nuri YILMAZ Aug 13 '13 at 13:15
  • This doesn't appear that it'd work as the value of the input is going to be set to the label's caption. Notice that MattSlay's version uses two different lists of params. You could get rid of the copies of ModelType and ModelProperty and access PropertyValue as {3} – Robert Dec 31 '13 at 13:58
  • @Robert I was simply trying to demonstrate that you could get the efficiency of TagBuilder and code maintainability by using a single call to the AppendFormat instead of multiple calls to AppendFormat like MattSlay. In addition to a single call to AppendFormat, notice how C#'s String literal allows you to define the string formatted with spaces for readability. Again, I was just trying to improve on MattSlay's method :) – Salman Hasrat Khan Jan 02 '14 at 09:31
  • No, I agree with you that it is very clean this way, just saying you currently have the input's value set to the label's text. – Robert Jan 02 '14 at 13:20
  • Ah! I see what you mean't there. Even Matt Slay had the exact same mistake, since I just copied his and improved it, I didn't notice the change... I'll fix it. – Salman Hasrat Khan Jan 03 '14 at 06:26