{"id":4511,"date":"2016-09-27T09:53:07","date_gmt":"2016-09-27T08:53:07","guid":{"rendered":"https:\/\/u-labs.de\/portal\/?p=4511"},"modified":"2016-09-03T10:08:51","modified_gmt":"2016-09-03T09:08:51","slug":"asp-net-core-taghelper-statt-urlhelper-fuer-sauberen-und-lesbaren-code","status":"publish","type":"post","link":"https:\/\/u-labs.de\/portal\/asp-net-core-taghelper-statt-urlhelper-fuer-sauberen-und-lesbaren-code\/","title":{"rendered":"ASP.NET Core: TagHelper statt UrlHelper f\u00fcr sauberen und lesbaren Code"},"content":{"rendered":"<p>Im alten ASP.NET MVC gab es eine recht umfangreiche Sammlung an UrlHelpern. Sie wurden zur Generierung von Links und Formular-Elementen wie Labeln oder Textboxen in Razor-Ansichten genutzt. Ihr Vorteil bestand darin, dass die entsprechenden HTML-Elemente automatisch auf Basis der genutzten Komponenten (Model, Route usw) generiert wurden. In einem Formular gen\u00fcgte es daher, beispielsweise den Anzeigetext nur im Model zu \u00e4ndern &#8211; durch die UrlHelper wurde dieser automatisch in der Ansicht ebenfalls gesetzt.<\/p>\n<p>Allerdings gibt es auch Nachteile: Sie bringen zus\u00e4tzliche Komplexit\u00e4t und verschlechtern dies Lesbarkeit sowie das Verst\u00e4ndnis. URL-Parameter und Attribute wie Klassen, Ids oder Data-Felder m\u00fcssen als anonymes Objekt \u00fcbergeben werden. Sp\u00e4testens an einem komplexeren Beispiel wird dies klar:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n@Html.ActionLink(&quot;Film hinzuf\u00fcgen&quot;, &quot;Add&quot;, &quot;Movie&quot;, new { @categoryId = 4  }, new { @class = &quot;text-center&quot;, @id = &quot;add-movie&quot; })\r\n<\/pre>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;a href=&quot;\/Movie\/Add?categoryId=4&quot; class=&quot;text-center&quot; id=&quot;add-movie&quot;&gt;Film hinzuf\u00fcgen&lt;\/a&gt;\r\n<\/pre>\n<p>Insbesondere f\u00fcr Entwickler die nicht damit vertraut sind ist h\u00e4ufig unklar, was genau eine solche Funktion macht.\u00a0Dies kann beispielsweise ein Problem werden, wenn ein Designer sich um die Optik einer solchen Anwendung k\u00fcmmern soll.<\/p>\n<h4><strong>Neue TagHelper betten sich in HTML ein<\/strong><\/h4>\n<p>Im neuen ASP.NET Core hat auch Microsoft dies erkannt und ersetzt daher die UrlHelper durch TagHelper. Klingt wie fast das gleiche? Keineswegs: Die statischen C# Klassen wurden komplett entfernt. Stattdessen schreibt man normales HTML, und kann darin bestimmte Attribute benutzen. Sie beginnen alle mit dem Pr\u00e4fix\u00a0<strong>asp-<\/strong> wie etwa <strong>asp-controller<\/strong> und werden zur Laufzeit entsprechend ersetzt.<\/p>\n<p>Schauen wir uns ein Beispiel an:<\/p>\n<p><b>Klassischer UrlHelper<\/b><\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n@Html.ActionLink(&quot;Film hinzuf\u00fcgen&quot;, &quot;Add&quot;, &quot;Movie&quot;)\r\n<\/pre>\n<p><b>Neuer ASP.NET Core TagHelper<\/b><\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;a asp-action=&quot;Add&quot; asp-controller=&quot;Movie&quot;&gt;Film hinzuf\u00fcgen&lt;\/a&gt;\r\n<\/pre>\n<p>In beiden F\u00e4llen erhalten wir als Ausgabe folgendes HTML:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;a href=&quot;\/Movie\/Add&quot;&gt;Film hinzuf\u00fcgen&lt;\/a&gt;\r\n<\/pre>\n<p>Der TagHelper generiert aus den Attributen\u00a0<em>Controller<\/em> und\u00a0<em>Action<\/em> das Linkziel, f\u00fcgt es in ein neues Attribute namens\u00a0<em>href<\/em> ein und entfernt die mit asp beginnenden Attribute. Der Razor-Quellcode ist deutlich besser lesbar, da er letztendlich aus bekanntem HTML mit zus\u00e4tzlichen Attributen besteht. Dass dies grunds\u00e4tzlich m\u00f6glich ist, d\u00fcrfte sp\u00e4testens seit den HTML5 data-Attributen niemanden mehr \u00fcberraschen.<\/p>\n<h4><strong>Weitere Beispiele f\u00fcr die Anwendung von TagHelpern<\/strong><\/h4>\n<p>Nicht nur bei einfachen Links k\u00f6nnen die neuen TagHelper genutzt werden. Microsoft hat\u00a0sie konsequent integriert, sodass sie die alten UrlHelper vollst\u00e4ndig ersetzen k\u00f6nnen. Im folgenden m\u00f6chte ich diese mit jeweils einem kurzen Beispiel vorstellen.<\/p>\n<h5><strong>Formulare<\/strong><\/h5>\n<p>Web-Formulare waren bislang besonders UrlHelper-Lastig. Im schlimmsten Falle bestanden sie bis auf den Submit-Button vollst\u00e4ndig daraus. Auch hier lassen sich nun Controller und Action als Attribut angeben:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;form asp-action=&quot;Add&quot; asp-controller=&quot;Movie&quot; asp-anti-forgery=&quot;true&quot;&gt;&lt;\/form&gt;\r\n<\/pre>\n<p>Das Attribut <em>asp-anti-forgery<\/em> ersetzt <em>@Html.AntiForgeryToken<\/em>, und f\u00fcgt ein unsichtbares Eingabefeld mit einem Validierungstoken ein. XSRF-Attacken werden so verhindert.<\/p>\n<h5><strong>Labels und Eingabefelder f\u00fcr Model-Attribute<\/strong><\/h5>\n<p>Innerhalb von Formularen werden h\u00e4ufig Labels und Eingabefelder f\u00fcr die Eigenschaften des jeweiligen Models verwendet. Das\u00a0<em>asp-for<\/em> Attribute legt fest, mit welcher Eigenschaft das Element verkn\u00fcpft ist.<\/p>\n<p>Zur Verdeutlichung ein sehr einfaches Model eines Filmes:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nclass Movie {\r\n    &#x5B;Display(Name = &quot;Filmtitel&quot;)]\r\n    public int Title { get; set; }\r\n}\r\n<\/pre>\n<p>Label und Eingabefeld erstellen wir wie folgt:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n@model Movie \r\n\r\n&lt;label asp-for=&quot;Title&quot;&gt;&lt;\/label&gt;\r\n&lt;input type=&quot;text&quot; asp-for=&quot;Title&quot; \/&gt;\r\n<\/pre>\n<p>Nach dem selben Prinzip kann ein beliebiges Element mit <em>asp-validate-for<\/em> zur Validierung erstellt werden, beispielsweise ein div:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;div asp-validate-for=&quot;Title&quot;&gt;&lt;\/label&gt;\r\n<\/pre>\n<p>Die bisher verwendeten Lambda-Attribute wie <em>model =&gt; model.Title<\/em> sind dem Rotstift zum Opfer gefallen. Doch dies hei\u00dft nicht, dass nun reine Strings ohne Validierung angegeben werden, denn Visual Studio bietet anhand des Models eine Autovervollst\u00e4ndigung:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/3906Nw.jpg\" \/><\/p>\n<p>Au\u00dferdem findet eine Validierung statt. Werden Felder angegeben die im Model nicht existieren, wird ein regul\u00e4rer Fehler erzeugt &#8211; Wie bei normalem C# Code, der auf nicht existierende Eigenschaften zugreift.<\/p>\n<p>ASP.NET Core \u00fcberl\u00e4sst dem Entwickler die freie Wahl, welche Elemente er verwenden m\u00f6chte. Bei den bisherigen Hilfsklassen konnten nur vordefinierte Elemente (InputFor, HiddenFor usw.) verwendet werden. Beispielsweise kann eine Textarea genutzt werden, wenn ein gr\u00f6\u00dferes Eingabefeld notwendig ist. Als positiver Nebeneffekt k\u00f6nnen alle HTML-Elemente beliebig Verschachtelt werden. Dies war bisher nur \u00fcber eigene Hilfsklassen m\u00f6glich.<\/p>\n<h5><strong>Auswahllisten (select)<\/strong><\/h5>\n<p>Bei Auswahllisten hat der Entwickler nun die Wahl zwischen statischen Eintr\u00e4gen oder dynamischen &#8211; denn Verschachtelungen sind wie eben erw\u00e4hnt in ASP.NET Core kein Problem mehr. Im folgenden Beispiel erzeugen wir ein Model <em>Category<\/em> und f\u00fcgen eine Liste davon zu unserem Film-Model hinzu:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;select asp-for=&quot;Categorys&quot;&gt;\r\n    &lt;option value=&quot;1&quot;&gt;Action&lt;\/option&gt;\r\n    &lt;option value=&quot;2&quot;&gt;Drama&lt;\/option&gt;\r\n    &lt;option value=&quot;3&quot;&gt;Kom\u00f6die&lt;\/option&gt;\r\n&lt;\/select&gt;\r\n<\/pre>\n<p>Nat\u00fcrlich k\u00f6nnen die Eintr\u00e4ge nach wie vor dynamisch generiert werden, etwa von einer Datenbank geladen. Dabei unterst\u00fctzt das\u00a0<em>asp-items<\/em> Attribute. Im folgenden erzeugen wir die gleiche Auswahlliste mit einer Liste von Kategorien:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n@{ \r\n    var categorys = new List&lt;Category&gt;() {\r\n        new Category(1, &quot;Action&quot;),\r\n        new Category(2, &quot;Drama&quot;),\r\n        new Category(3, &quot;Kom\u00f6die&quot;)\r\n    };\r\n    var selectListItems = new SelectList(categorys, &quot;Id&quot;, &quot;Name&quot;);\r\n}\r\n<\/pre>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;select asp-for=&quot;Categorys&quot; asp-items=&quot;selectListItems&quot;&gt;&lt;\/select&gt;\r\n<\/pre>\n<p>Die aus dem alten ASP.NET MVC Framework bekannte Hilfsklasse <em>SelectList<\/em> ist erhalten geblieben. Auch die Verwendung unterscheidet sich nicht: Im ersten Parameter wird eine generische Liste \u00fcbergeben. Der zweite definiert die Bezeichnung des Attributes, welches als <em>value<\/em> Attribute in den HTML-Select Elementen gesetzt werden soll. Zu guter Letzt folgt die sichtbare Bezeichnung des Eintrages.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im alten ASP.NET MVC gab es eine recht umfangreiche Sammlung an UrlHelpern. Sie wurden zur Generierung von Links und Formular-Elementen wie Labeln oder Textboxen in Razor-Ansichten genutzt. Ihr Vorteil bestand darin, dass die entsprechenden HTML-Elemente automatisch auf Basis der genutzten Komponenten (Model, Route usw) generiert wurden. In einem Formular gen\u00fcgte es daher, beispielsweise den Anzeigetext &#8230;<\/p>\n","protected":false},"author":5,"featured_media":4552,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61],"tags":[522,537],"class_list":["post-4511","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-softwareentwicklung","tag-asp-net-core","tag-asp-net-core-1-0"],"_links":{"self":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/4511","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/comments?post=4511"}],"version-history":[{"count":40,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/4511\/revisions"}],"predecessor-version":[{"id":4553,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/4511\/revisions\/4553"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media\/4552"}],"wp:attachment":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media?parent=4511"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/categories?post=4511"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/tags?post=4511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}