Nested TagBuilder -as TagBuilderTree-
Asked Answered
B

3

31

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?

Betthezel answered 10/2, 2011 at 14:21 Comment(0)
H
51

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();
}
Hoem answered 10/2, 2011 at 15:1 Comment(8)
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 usersBetthezel
You mean WebPages, not Razor. It's not necessary for all developers; you can use static helpers instead.Hoem
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?Betthezel
By nesting tagbuilders you essentially ruin the performance of the internally used StringBuilder class right?Canara
@Mvision: Correct. If the tags might be lengthy, XElement would probably be faster.Hoem
I added an example, hope that's ok.Calyptrogen
@MrBoJangles: A StringBuilder would be more efficient.Hoem
Good to know. This is an example from code that builds a single <select> with maybe half a dozen options maximum, so the improved readability is more important to me. A fully-fledged tree structure might be more of a performance concern. If someone would like to add a StringBuilder example, that might be nice to compare/contrast.Calyptrogen
T
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.

Tips answered 12/12, 2012 at 21:29 Comment(2)
#1 is also a possible avenue for script injection if you're not careful to sanitize, I would just like to add.Crankpin
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.Ivy
P
-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!

Pricket answered 13/8, 2013 at 12:43 Comment(5)
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.Betthezel
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}Choli
@Choli 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 :)Pricket
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.Choli
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.Pricket

© 2022 - 2024 — McMap. All rights reserved.