ASP.NET MVC 6 Tag Helper について
by ASP.NET Advent Calendar 2015
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:
MVC 6 Tag Helper:
※生成結果の 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 も併用して記述することができます。
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 クラスのプロパティとして定義します。
コントロール以外の使い道もあります。例えば、プロジェクトテンプレートの _Layout.cshtml に記述されている EnvironmentTagHelper があります。これは、環境によって内部の要素を出力するかどうかを制御します。
2. Tag Helper の機能
Tag Helper の機能を Tips として書いています。
シンタックスハイライトとインテリセンス
↓の GIF のように、Tag Helper は、シンタックスハイライトとインテリセンスが対応しています。
対応する Tag Helper の実装クラスの情報がツールチップで表示されます。おそらく"Microsoft.AspNet.Tooling.Razor" パッケージによるもの。
モデルのバインド
次のような RegisterViewModel.cs の Email プロパティに対応するコントロールを配置したいときは、「asp-for=”Email”」と書きます。
今までは、「@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:
試しにこの記述を Register.cshtml に移動します。そして、この宣言を消したり・追加したり、してみます。シンタックスハイライトも追従します。もちろん HTML の出力結果も異なります。
独自の Tag Helper を作成した際には、アセンブリ名を @addTagHelper “*, hogehoge” と宣言することになります。また、@removeTagHelper で除外もできます。
Tag Helper を無効にする
Tag Helper として定義してあるけど、ここだけはそのまま HTML として出力したい、という場合には、要素前に「!」を付けます。
@tagHelperPrefix
Tag Helper を表すためのプリフィックスを定義できます。例えば、「@tagHelperPrefix “tag:”」と宣言すると、「tag:input」要素が Tag Helper として認識されます。Tag Helper かそうでないかを明確にしたいときに使えそうです。
機能の Tips は以上です。
独自の Tag Helper を作る
Tag Helper を自分で作る時はどんな感じかなーと見てみました。Tag Helper の作成には 2 種類あって、要素を定義して Tag Helper とする方法と(<myTag />)、要素ではなく、属性を指定して Tag Helper とする方法(<span myAttribute>)があります。
WebsiteInformationTagHelper
要素自体を定義してみます。
Web サイトの情報を表す WebsiteContext クラスを定義し、<website-information /> 要素で出力する WebsiteInformationTagHelper クラスを作成します。
.cshtml はこんな感じ。
(info の値が C# コードとしてハイライトされている…)
ブラウザから出力結果を確認すると、リストで表示されます。
BoldTagHelper
属性を指定する方法です。「mybold」属性がつく要素を、太字で出力する BoldTagHelper を作成してみます。
既存の要素の属性として付与すると、strong 要素で囲って出力されます。
出力結果↓
<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 の属性が書きにくいこと。
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# で表すこと。
開閉タグのために C# の using を使うのも変ですが、HTML 要素を @Html.~で書くことにより、“ある要素はある要素の中で書かなければいけない”等の HTML 構文規約に違反したと(IDEから)みなされることが多々ありました。
独自の Helper
独自 Html Helper の実装に疲労することがよくありました。
例えば、独自の Html Helper を作成し、しばらく経った後、拡張する必要がでたとき。とある属性をちょっと追加したいがために、Html Helper のメソッドの引数を増やし、対応する箇所のビューを編集し…といったことがあります。引数がたくさんある同じような Html Helper ができあがったり。拡張性の高い設計をすればいい話ではありますが、Tag Helper では柔軟に解決できるのではないかと思います。