Etiquetas HTML en cascada usando C #

En el siguiente proceso de escribir una aplicación web para ASP.NET MVC usando Bootstrap, me sorprendí pensando que la inevitable creación de etiquetas HTML podría sustraerse. No se trata de un conjunto de controles de usuario para expandir el espacio Html. *, Sino de lo que se encuentra un poco más lejos. Por prisa, te sugiero que mires aquí (GitHub) , y por lo demás, bienvenido a cat.

Desafío


Hay una etiqueta HTML que contiene el nombre, clases, estilos, atributos, etc. En .NET, para la creación "manual" de esta belleza, se supone que usa la clase TagBuilder y la llena gradualmente con los meta y datos necesarios.

Pero!

El uso regular de esta clase me pareció demasiado triste. Constante * .AddCssClass, * .Attributes.Add, * .MergeAttribute y * .ToString (TagRenderMode.SelfClosing) - en algún momento comienzan a molestar con su paso a paso.

Aquí hay un ejemplo de cómo se ve un elemento de botón estándar:

<!-- HTML --> <button type="button" class="btn btn-success">Success</button> 

 // C# var tb = new TagBuilder("button"); tb.Attributes.Add("type", "button"); tb.AddCssClass("btn"); tb.AddCssClass("btn-success"); tb.SetInnerText("Success"); var htmlString = tb.ToString(TagRenderMode.Normal); 

Agregamos aquí que a veces las etiquetas HTML requieren anidamiento, lo que significa que se requieren uno o varios * .InnerHtml con un parámetro, cada uno de los cuales a su vez debe crearse de la misma manera: largo-dimensionalmente-paso a paso - queda claro que quieres algo Algo menos rutinario.

Así nació la clase TagDecorator .

El problema declarado que me gustaría resolver es el siguiente: para simplificar la creación de etiquetas HTML, alejarme paso a paso y organizar el anidamiento natural de la jerarquía HTML.

Solución


Enlace: TagDecorator

En la etapa inicial, la solución consistía en dos clases, pero posteriormente se les agregó otra:

TagDecorator es la clase de trabajo principal, que es responsable de convertir el texto plano en una clase que representa una etiqueta (TagWrapper), a la que se pueden adjuntar extensiones adicionales. En el ejemplo existente, hay funciones generales AddAttribute, AddCss y funciones privadas AddType, AddName, AddId, que crean atributos específicos.

TagWrapper es una clase que representa una etiqueta. Fue creado para alejarse completamente de TagBuilder siempre que sea posible y las nuevas extensiones en IntelliSense no se confundieron con las propiedades de la clase TagBuilder.

Etiquetas : la clase necesitaba separar las etiquetas de inicio / fin para que sea posible implementar los bloques HTML de trama utilizados en RazorView

 @using(Html.SuchExtension) { //... }. 

Ejemplos


En el ejemplo del botón indicado, las ventajas de usar TagDecorator no son tan obvias:

 var htmlString = "button".ToTag() .AddType("button") .AddCss(new[] {"btn", "btn-success"}) .SetText("Success") .ToString(); 

Pero ahora con el ejemplo de la tarjeta Bootstrap , todo se está volviendo mucho más agradable a la vista:

Usando 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); 

Versión con TagDecorator
 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(); 

Resultados


Contras


El principal de los cuales creo es que la implementación en cascada a veces desplaza el código a la derecha, a menudo va más allá de la visión del desarrollador, y cuanto más compleja es la jerarquía, más fuerte

Pros


+ Línea de códigos - reducida. En los elementos más simples, hay una ganancia de aproximadamente 1-2 líneas, en un árbol HTML complejo, aproximadamente 1/3 por analogía si usa TagBuilder.

+ Visibilidad: puede ver claramente dónde y qué tipo de anidamiento. Todo es jerárquicamente intuitivo y más fácil de entender.

+ Extensibilidad: se necesita algún caso / atributo específico, solo agregue la extensión. Se necesita una verificación de condición: agregue la extensión.

Posibles mejoras.


Al principio, pensé en crear etiquetas completamente especializadas basadas en estas clases que permitieran solo ciertas extensiones en la información sobre herramientas; por ejemplo, eliminar la información sobre herramientas de extensión AddReference en la etiqueta del botón, pero luego abandoné estos planes por el bien de la universalidad. Pero en general, esta solución ahora me ayuda mucho en mis proyectos.

También se suponía que debía crear un paquete NuGet, pero por falta de tiempo, todo se pospuso.

Source: https://habr.com/ru/post/es416459/


All Articles