In the next process of writing a web application under ASP.NET MVC using Bootstrap, I caught myself thinking that the inevitable creation of HTML tags could have been shortened. This is not about a set of user controls to expand the Html. * Space, but about what lies a little bit. For a hurry, I suggest to look
here (GitHub) , and for the rest, welcome under cat.
Task
There is an HTML tag containing the name, classes, styles, attributes, etc. In .NET, for the “manual” creation of this beauty, it is assumed to use the
TagBuilder class and gradually fill it with the necessary meta and simple data.
But!
Regular use of this class seemed to me too dreary. Permanent * .AddCssClass, * .Attributes.Add, * .MergeAttribute and * .ToString (TagRenderMode.SelfClosing) - at some point they start to annoy with their step-by-step.
For example, what a standard button element looks like:
<button type="button" class="btn btn-success">Success</button>
Add here the fact that sometimes HTML tags require nesting, which means that you need one or many * .InnerHtml with a parameter, each of which in turn should be created in the same way - long-dimensionally step-by-step - it becomes clear that you want something less routine.
This is how the
TagDecorator class was born.
The formulated task that I would like to solve sounds as follows -
to simplify the creation of HTML tags, move away from the steps and organize the natural nesting of the HTML hierarchy.Decision
Link:
TagDecoratorAt the initial stage, the solution consisted of two classes, but one more was subsequently added to them:
TagDecorator is the main working class that is responsible for turning plain text into a class representing a tag (TagWrapper), to which additional extensions can be attached. In the existing example there are both general functions AddAttribute, AddCss, and private functions AddType, AddName, AddId - creating specific attributes.
TagWrapper - the class that represents the tag. It was created in order to fully depart from TagBuilder and new extensions in IntelliSense were not confused with the properties of the TagBuilder class.
Tags - the class needed to separate the begin / end tags to enable the implementation of the HTML enclosing blocks used in RazorView
@using(Html.SuchExtension) {
Examples
In this example, the buttons of the advantages of the result of using TagDecorator are somewhat not obvious:
var htmlString = "button".ToTag() .AddType("button") .AddCss(new[] {"btn", "btn-success"}) .SetText("Success") .ToString();
But now using the example of the
Bootstrap card - everything is already becoming much more pleasant to the eye:
Using TagBuilder
var divMain = new TagBuilder("div"); divMain.AddCssClass("card"); divMain.Attributes.Add("style", "width: 18rem;"); var img = new TagBuilder("img"); img.AddCssClass("card-img-top"); img.Attributes.Add("src", "..."); img.Attributes.Add("alt", "Card image cap"); var divBody = new TagBuilder("div"); divBody.AddCssClass("card-body"); var h = new TagBuilder("h5"); h.AddCssClass("card-title"); h.SetInnerText("Card title"); var p = new TagBuilder("p"); p.AddCssClass("card-text"); p.SetInnerText("Some quick example text to build on the card title and make up the bulk of the card's content."); var a = new TagBuilder("a"); a.Attributes.Add("href", "#"); a.AddCssClass("btn"); a.AddCssClass("btn-primary"); a.SetInnerText("Go somewhere"); divBody.InnerHtml += h.ToString(TagRenderMode.Normal); divBody.InnerHtml += p.ToString(TagRenderMode.Normal); divBody.InnerHtml += a.ToString(TagRenderMode.Normal); divMain.InnerHtml += img.ToString(TagRenderMode.Normal); divMain.InnerHtml += divBody.ToString(TagRenderMode.Normal); return divMain.ToString(TagRenderMode.Normal);
TagDecorator Version
var htmlString = "div".ToTag() .AddCss("card") .AddAttribute("style", "width: 18rem;") .InnerHtml(new [] { "img".ToTag() .AddCss("card-img-top") .AddAttributes(new[] {new[] {"src", "..."}, new[] {"alt", "Card image cap"}}), "div".ToTag() .AddCss("card-body") .InnerHtml(new [] { "h5".ToTag().AddCss("card-title").SetText("Card title"), "p".ToTag().AddCss("card-text").SetText("Some quick example text to build on the card title and make up the bulk of the card's content."), "a".ToTag().AddCss(new[] {"btn", "btn-primary"}).AddAttribute("href", "#").SetText("Go somewhere") }) }).ToString();
results
Minuses
The main one of which I think is that cascading implementation sometimes shifts the code to the right, often taking the developer’s vision beyond the limits of the developer’s vision, and the more complex the hierarchy is, the stronger
pros
+ Line of codes - shrinking. In the simplest elements there is a gain of about 1-2 lines, in a complex HTML tree - about 1/3 by analogy if you use TagBuilder.
+ Visibility - it is clearly visible that where and what nesting. Everything is hierarchically intuitive and easier to understand.
+ Extensibility - some specific case / attribute is needed - just add an Extension. Required check on the condition - add Extension.
Possible improvements
At first, I thought about creating fully specialized tags based on these classes that would allow only certain extensions in the tooltip - for example, to remove the button of the AddReference extension in the button tag, but later refused these plans for the sake of universality. But in general - this solution now greatly helps me in my projects.
It was also supposed to create a NuGet package, but due to lack of time, everything is postponed.