Nested TagBuilder -as TagBuilderTree-

2019-03-14 14:27发布

问题:

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?

回答1:

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();
}


回答2:

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.



回答3:

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!