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:
<button type="button" class="btn btn-success">Success</button>
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:
TagDecoratorNo 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.