読者です 読者をやめる 読者になる 読者になる

miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET MVC 6 Tag Helper について

by ASP.NET Advent Calendar 2015

f:id:miso_soup3:20151202225044p:plain

Tag Helper がどんなものかみてみました。

Tag Helper は、ASP.NET 5/ASP.NET MVC 6 に新しく登場する機能の1つです。ASP.NET MVC 6 のビューの記述は、これまでと同じように行う(Razor 記法で .cshtml ファイルを書く)とはいえ、MVC 6 で開発するならば知っておきたい機能かなと思います。Tag Helper の登場はビューの開発に少なからず影響するはずです。

Helper とはいえ、ビューの記述言語が変わったような(Razor はそのままなのですが)インパクトで、シンタックスハイライトとインテリセンスが面白いなーというのが感想です。

目次
  • 1. Tag Helper とは
  • 2. Tag Helper の機能
  • 3. 独自の Tag Helper を作る
  • 参考サイト

※現在確認している環境は Visual Studio 2015 Update 1 と、ASP.NET 5 RC 1 です。将来でる RTM とは違う可能性があります。

1. Tag Helper とは

簡単にいえば、これまでの Html Helper の次のバージョンです。

下は、アカウント登録画面を例にしたビューです。上が ASP.NET MVC 5 での方法、下が、ASP.NET MVC 6 での方法です。どちらも Razor 記法を使用しています、Html 要素のレンダリングの記述を、前者は Html Helper、後者は Tag Helper で書いてあります。

これまで Html Helper:
f:id:miso_soup3:20151202222435p:plain

MVC 6 Tag Helper:
f:id:miso_soup3:20151202222442p:plain

※生成結果の HTML が全く同じというわけではありません。

下の画像で、紫色で書かれているのが Tag Helper です。一見、HTML 構文と同じ書き方ですが、レンダリングする際に対応する C# コードが処理され HTML が出力されます。 HTML と同じ書き方で、サーバーサイドの処理を表すのが Tag Helper です。

HTML なのか Tag Helper なのか判断がつかないのではないか?と最初は思います。とあるルールにより Tag Helper か HTML か(サーバーサイドで処理を行い HTML を出力するか、そのまま HTML として出力するか)区別され、Visual Studio のシンタックスハイライトで色分けされ、開発者からみて分かるようになっています。

Html Helper も使える

ASP.NET MVC 6 では、Html Helper も併用して記述することができます。

f:id:miso_soup3:20151202222558p:plain

ASP.NET MVC 6 で開発するけど Tag Helper を使わない、という選択肢も可能ですし、両方使うことも可能です。
使い分けとしては、Tag Helper は HTML 要素・部品を出力するもの、という位置付けなので、例えば @Html.Partial() (部分ビューとして別のファイルを読み込む)は、Tag Helper として表すことはないと思います。

予め用意されている Tag Helper

画像にあった input, label, form などは、ASP.NET MVC 6 に予め組み込まれている Tag Helper です。GitHub でMicrosoft.AspNet.Mvc.TagHelpers 配下に「***TagHelper」クラスがたくさんあることが確認できます。これらをデフォルトで使うことができます。自分で Tag Helper を定義する際は、HogeHogeTagHelper とサフィックスが TagHelper になるのが一般的です。「asp-for」といった属性は、TagHelper クラスのプロパティとして定義します。

f:id:miso_soup3:20151202222653p:plain

コントロール以外の使い道もあります。例えば、プロジェクトテンプレートの _Layout.cshtml に記述されている EnvironmentTagHelper があります。これは、環境によって内部の要素を出力するかどうかを制御します。

f:id:miso_soup3:20151202222709p:plain

2. Tag Helper の機能

Tag Helper の機能を Tips として書いています。

シンタックスハイライトとインテリセンス

↓の GIF のように、Tag Helper は、シンタックスハイライトとインテリセンスが対応しています。

f:id:miso_soup3:20151202222734g:plain

対応する Tag Helper の実装クラスの情報がツールチップで表示されます。おそらく"Microsoft.AspNet.Tooling.Razor" パッケージによるもの。

モデルのバインド

次のような RegisterViewModel.cs の Email プロパティに対応するコントロールを配置したいときは、「asp-for=”Email”」と書きます。

f:id:miso_soup3:20151202222807p:plain

f:id:miso_soup3:20151202222814p:plain

今までは、「@Html.TextBoxFor(m => m.Email)」のように、C# の文法に準じて書いていましたが、Tag Helper では、「Email」という文字列で、対応するモデルのプロパティと認識します。シンタックスハイライトでも、C# コードを表す灰色の背景色になっています。(画像のように @model RegisterViewModel と宣言する必要があります。)

@addTagHelper

Tag Helper を記述するには、@addTagHelper でアセンブリ名を宣言する必要があります。ASP.NET MVC 6 のプロジェクトテンプレートでは、_ViewImports.cshtml に記述されています。これで全ビューにて予め用意されている Tag Helper を適用することができます。

_ViewImports.cshtml:
f:id:miso_soup3:20151202222849p:plain

試しにこの記述を Register.cshtml に移動します。そして、この宣言を消したり・追加したり、してみます。シンタックスハイライトも追従します。もちろん HTML の出力結果も異なります。

f:id:miso_soup3:20151202222915g:plain

独自の Tag Helper を作成した際には、アセンブリ名を @addTagHelper “*, hogehoge” と宣言することになります。また、@removeTagHelper で除外もできます。

Tag Helper を無効にする

Tag Helper として定義してあるけど、ここだけはそのまま HTML として出力したい、という場合には、要素前に「!」を付けます。

f:id:miso_soup3:20151202222936p:plain

@tagHelperPrefix

Tag Helper を表すためのプリフィックスを定義できます。例えば、「@tagHelperPrefix “tag:”」と宣言すると、「tag:input」要素が Tag Helper として認識されます。Tag Helper かそうでないかを明確にしたいときに使えそうです。

f:id:miso_soup3:20151202223000p:plain

機能の Tips は以上です。

独自の Tag Helper を作る

Tag Helper を自分で作る時はどんな感じかなーと見てみました。Tag Helper の作成には 2 種類あって、要素を定義して Tag Helper とする方法と(<myTag />)、要素ではなく、属性を指定して Tag Helper とする方法(<span myAttribute>)があります。

WebsiteInformationTagHelper

要素自体を定義してみます。

Web サイトの情報を表す WebsiteContext クラスを定義し、<website-information /> 要素で出力する WebsiteInformationTagHelper クラスを作成します。

f:id:miso_soup3:20151202223219p:plain

.cshtml はこんな感じ。

f:id:miso_soup3:20151202223228p:plain
(info の値が C# コードとしてハイライトされている…)

ブラウザから出力結果を確認すると、リストで表示されます。

f:id:miso_soup3:20151202223240p:plain

BoldTagHelper

属性を指定する方法です。「mybold」属性がつく要素を、太字で出力する BoldTagHelper を作成してみます。

f:id:miso_soup3:20151202223515p:plain

既存の要素の属性として付与すると、strong 要素で囲って出力されます。

f:id:miso_soup3:20151202223528p:plain
出力結果↓

<span mybold><strong>hello</strong></span>
<p mybold><strong>ho</strong></p>
<div mybold><strong>hi</strong></div>


他、できることとしては、出力するタグを任意のタグに変更するのはもちろん、プロパティの値の参照、要素内にある Content を変更したりできます。

適当に 独自 Tag Helper の例をあげます。ググると記事もでてきます。

  • モデルにあるMarkdown、あるいは要素内にある Markdown を Html として変換する
  • 環境によって Image のパスを CDN に変換する
  • 要素の中にある特定の文字列を変換する(URL だったら a タグに置き換えたり)
  • @if 文を省略
  • サードパーティ用の Tag Helper が登場したり? bootstrapとか。

3. Html Helper と Tag Helper の違い

まだ Tag Helper で充分な開発をしていないので、ここからは予想になります。

Tag Helper により、これまでよりフロントエンド寄り・HTML 寄りにビューの開発ができるようになります。

2011年頃に ASP.NET MVC 3/Razor が登場してから数年の間、Html Helper では困る事が積み重なったと思われます。ASP.NET MVC が出たときは、「HTML を完全制御できるようになった」という特徴がよく言われてたのですが…HTML 文中にサーバーサイドの処理を挟む、というのはまだまだ、フロントエンド寄りになる必要があるらしい。Bootstrap をはじめ、AngularJS、Web Component、Polymer の登場も影響しているのではないかと思います。

確かに振り返ると、サーバーとは関係のない HTML を書いているはずなのに、なぜ C# のコードを書いているんだろう…といったことがよくありました。

Html Helper であるあるな問題例をいくつかあげます。

フロントエンドのインテリセンスが使えない

まず思いつくのが、Html Helper では、HTML の属性が書きにくいこと。

f:id:miso_soup3:20151202223828p:plain

Html Helper で class 属性を書きたいとき、「@class=”hoge”」とC# コードとしてかかなければいけません。また属性名は、C# の制限により @ を付ける必要があったり、ハイフンが使えなかったりします。(参照:http://stackoverflow.com/questions/4108943/actionlink-htmlattributes)インテリセンスも活用することができません。Tag Helper では、画像のようにインテリセンスの恩恵を受けられます。Visual Studio と Web Essentials のインテリセンスがカバーする範囲は CSS から JS フレームワークまで広範囲です。

form タグ、HTML 構文のルール

次に思いつくのが、開閉 タグを using 等 C# で表すこと。

f:id:miso_soup3:20151202224012p:plain

開閉タグのために C# の using を使うのも変ですが、HTML 要素を @Html.~で書くことにより、“ある要素はある要素の中で書かなければいけない”等の HTML 構文規約に違反したと(IDEから)みなされることが多々ありました。

独自の Helper

独自 Html Helper の実装に疲労することがよくありました。
例えば、独自の Html Helper を作成し、しばらく経った後、拡張する必要がでたとき。とある属性をちょっと追加したいがために、Html Helper のメソッドの引数を増やし、対応する箇所のビューを編集し…といったことがあります。引数がたくさんある同じような Html Helper ができあがったり。拡張性の高い設計をすればいい話ではありますが、Tag Helper では柔軟に解決できるのではないかと思います。