Dans le processus suivant d'écriture d'une application Web pour ASP.NET MVC à l'aide de Bootstrap, je me suis surpris à penser que la création inévitable de balises HTML pouvait être soustraite. Il ne s'agit pas d'un ensemble de contrôles utilisateur pour étendre l'espace Html. *, Mais de ce qui se trouve un peu plus loin. Pour la hâte, je vous suggère de regarder
ici (GitHub) , et pour le reste, bienvenue au chat.
Défi
Il existe une balise HTML contenant le nom, les classes, les styles, les attributs, etc. Dans .NET, pour la création "manuelle" de cette beauté, il est supposé utiliser la classe
TagBuilder et la remplir progressivement avec les méta nécessaires et juste les données.
Mais!
L'utilisation régulière de cette classe me semblait trop morne. Constante * .AddCssClass, * .Attributes.Add, * .MergeAttribute et * .ToString (TagRenderMode.SelfClosing) - à un moment donné, commencez à ennuyer leur étape par étape.
Voici un exemple de ce à quoi ressemble un élément de bouton standard:
<button type="button" class="btn btn-success">Success</button>
Nous ajoutons ici que parfois les balises HTML nécessitent une imbrication, ce qui signifie qu'un ou plusieurs * .InnerHtml avec un paramètre sont requis, chacun à son tour devant être créé de la même manière - dimensionnellement pas à pas - il devient clair que vous voulez quelque chose quelque chose de moins routinier.
C'est ainsi que la classe
TagDecorator est née.
Le problème déclaré que je voudrais résoudre est le suivant -
pour simplifier la création de balises HTML, s'éloigner de l'étape par étape et organiser l'imbrication naturelle de la hiérarchie HTML.Solution
Lien:
TagDecoratorAu stade initial, la solution consistait en deux classes, mais par la suite une autre y a été ajoutée:
TagDecorator est la principale classe ouvrière, qui est chargée de transformer le texte brut en une classe représentant une balise (TagWrapper), à laquelle des extensions supplémentaires peuvent être attachées. Dans l'exemple existant, il existe à la fois des fonctions générales AddAttribute, AddCss et des fonctions privées AddType, AddName, AddId - créant des attributs spécifiques.
TagWrapper est une classe représentant une balise. Il a été créé pour s'éloigner complètement de TagBuilder autant que possible et les nouvelles extensions dans IntelliSense n'ont pas été confondues avec les propriétés de la classe TagBuilder.
Balises - la classe nécessaire pour séparer les balises de début / fin pour permettre l'implémentation des blocs HTML de cadrage utilisés dans RazorView
@using(Html.SuchExtension) {
Des exemples
Dans l'exemple de bouton indiqué, les avantages de l'utilisation de TagDecorator ne sont pas si évidents:
var htmlString = "button".ToTag() .AddType("button") .AddCss(new[] {"btn", "btn-success"}) .SetText("Success") .ToString();
Mais maintenant, avec l'exemple de la
carte Bootstrap - tout devient déjà beaucoup plus agréable à l'œil:
Utilisation de 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);
Version avec 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();
Résultats
Inconvénients
Selon moi, la principale est que la mise en œuvre en cascade déplace parfois le code vers la droite, allant souvent au-delà de la vision du développeur, et plus la hiérarchie est complexe, plus elle est forte
Avantages
+ Ligne de codes - réduite. Dans les éléments les plus simples, il y a un gain d'environ 1-2 lignes, dans une arborescence HTML complexe - environ 1/3 par analogie si vous utilisez TagBuilder.
+ Visibilité - vous pouvez voir clairement où et quel type d'imbrication. Tout est hiérarchiquement intuitif et plus facile à comprendre.
+ Extensibilité - un cas / attribut spécifique est nécessaire - ajoutez simplement Extension. Une vérification de l'état est nécessaire - ajoutez l'extension.
Améliorations possibles
Au début, j'ai pensé à créer des balises complètement spécialisées basées sur ces classes qui n'autoriseraient que certaines extensions dans l'info-bulle - par exemple, supprimer l'info-bulle de l'extension AddReference dans la balise button, mais j'ai ensuite abandonné ces plans dans un souci d'universalité. Mais en général, cette solution m'aide beaucoup dans mes projets.
Il était également censé créer un package NuGet, mais par manque de temps - tout est reporté.