Tags HTML em cascata usando C #

No próximo processo de criação de um aplicativo Web para o ASP.NET MVC usando o Bootstrap, me peguei pensando que a inevitável criação de tags HTML poderia ser subtraída. Não se trata de um conjunto de controles de usuário para expandir o espaço Html. *, Mas sobre o que está um pouco mais longe. Por pressa, sugiro que você procure aqui (GitHub) e, quanto ao resto, seja bem-vindo ao gato.

Desafio


Há uma tag HTML que contém o nome, classes, estilos, atributos etc. No .NET, para a criação "manual" dessa beleza, supõe-se que você use a classe TagBuilder e a preencha gradualmente com os meta e justos dados necessários.

Mas!

O uso regular dessa classe me parecia muito sombrio. Constante * .AddCssClass, * .Attributes.Add, * .MergeAttribute e * .ToString (TagRenderMode.SelfClosing) - em algum momento começam a incomodar seu passo a passo.

Aqui está um exemplo da aparência de um elemento de botão padrão:

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

Acrescentamos aqui que, às vezes, as tags HTML requerem aninhamento, o que significa que um ou muitos * .InnerHtml com um parâmetro são necessários, cada um dos quais deve ser criado da mesma maneira - longamente dimensionalmente passo a passo - fica claro que você deseja algo algo menos rotineiro.

Foi assim que a classe TagDecorator nasceu.

O problema declarado que gostaria de resolver é o seguinte - para simplificar a criação de tags HTML, afastar-se passo a passo e organizar o aninhamento natural da hierarquia HTML.

Solução


Link: TagDecorator

No estágio inicial, a solução consistia em duas classes, mas posteriormente outra foi adicionada a elas:

TagDecorator é a principal classe trabalhadora, responsável por transformar o texto sem formatação em uma classe que representa uma tag (TagWrapper), à qual extensões adicionais podem ser anexadas. No exemplo existente, existem as funções gerais AddAttribute, AddCss e funções privadas AddType, AddName, AddId - criando atributos específicos.

TagWrapper é uma classe que representa uma tag. Foi criado para afastar-se completamente do TagBuilder sempre que possível e as novas extensões no IntelliSense não foram confundidas com as propriedades da classe TagBuilder.

Tags - a classe necessária para separar as tags de início / fim para possibilitar a implementação dos blocos HTML de enquadramento usados ​​no RazorView

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

Exemplos


No exemplo do botão indicado, as vantagens de usar o TagDecorator não são tão óbvias:

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

Mas agora com o exemplo do cartão Bootstrap - tudo já está se tornando muito mais agradável aos olhos:

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

Versão com 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


A principal das quais acredito é que a implementação em cascata às vezes muda o código para a direita, muitas vezes indo além da visão do desenvolvedor, e quanto mais complexa a hierarquia, mais forte

Prós


+ Linha de códigos - reduzida. Nos elementos mais simples, há um ganho de cerca de 1-2 linhas, em uma complexa árvore HTML - cerca de 1/3 por analogia, se você usar o TagBuilder.

+ Visibilidade - você pode ver claramente onde e que tipo de aninhamento. Tudo é hierarquicamente intuitivo e mais fácil de entender.

+ Extensibilidade - é necessário algum caso / atributo específico - basta adicionar Extension. É necessária uma verificação da condição - adicione Extensão.

Possíveis melhorias


No começo, pensei em criar tags completamente especializadas com base nessas classes que permitissem apenas determinadas extensões na dica de ferramenta - por exemplo, remova a dica de ferramenta de extensão AddReference na tag button, mas depois abandonou esses planos por uma questão de universalidade. Mas, em geral, essa solução agora me ajuda muito em meus projetos.

Também era para criar um pacote NuGet, mas pela falta de tempo - tudo é adiado.

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


All Articles