ASP.NET Core: タグヘルパーでのHTML編集方法

ASP.NET Coreで独自のタグを生成するためにTagHelperを使用します。
TagHelperでどのようにHTMLを生成できるかを説明します。

概要

  • ここではタグヘルパーで出力するHTMLの編集方法を説明します。
  • 実行環境はWindows10 + ASP.NET Core 3.1を前提としています。
  • タグヘルパーの基本的な実装方法を理解している前提で説明します。
  • タグヘルパーで生成されたHTML要素の厳密な確認を行いたい場合は、ブラウザのページソースで確認してください。
    Chromeの開発者ツール(F12)の場合、出力されたHTMLを見やすく整形してくれるのは良いのですが、生成されたコードと異なる表示になる場合があります。
  • 完全なソースコードはGitHubで公開しています。

基本的なHTML編集の仕組み

  • タグヘルパーでは、Process()メソッドの引数output(TagHelperOutput)のメソッドやプロパティを操作してHTMLを編集できます。
    HTMLを任意に編集するためには、TagHelperOutputのPreElement, PostElement, PreContent, Content, PostContentプロパティ(TagHelperContent型)を理解することが重要です。
    ここではまず、これらのプロパティを操作するタグヘルパーのサンプルを説明します。
    このタグヘルパーの入力となるHTMLと、タグヘルパーが出力するHTMLを次に示します。
    前述の各プロパティを操作することで、入力要素の前後への要素の追加や、入力要素に子要素を追加することができます。
    (出力例に関して、入力要素に対応する行はマークしています。)
  • タグヘルパーの入出力とTagHelperOutputの関係は次の通りです。

    • ここでは説明の便宜上、タグヘルパーの処理対象となるHTML要素を「入力要素」と表記します。
      タグヘルパーで出力するHTML要素に関して、入力要素に対応して既定で作成されるHTML要素を「メイン出力要素」、それ以外の要素を「その他出力要素」と表記します。
    • メイン出力要素の前後にdiv, span等の任意のHTML要素を挿入したい場合、Process()メソッドの引数output(TagHelperOutput)のPreElement, output.PostElementプロパティ(TagHelperContent)を使用します。
    • メイン出力要素の子要素としてHTML要素を挿入したい場合、同様に引数outputのPreContent, output.Content, output.PostContentプロパティ(TagHelperContent)を使用します。
      なお、タグヘルパーの対象がinput等の開始タグのみの要素(値を持たない要素)の場合、これらのプロパティを設定しても無視されます。
  • サンプルコードでは文字列でHTML要素を指定していますが、TagBuilderクラスを使用してHTMLを編集する方法があります。
    この方法は、煩雑な文字列操作は不要かつオブジェクト操作のみでHTMLを編集(Object-HTMLマッパー?)できるので、通常はTagBuilderを使用する方法がおすすめです。

TagHelperOutputを使ったHTML編集

  • TagHelperOutputを使用してメイン出力要素を編集する方法を説明します。
  • メイン出力要素以外を編集する場合、PreElement, PostElement, PreContent, Content, PostContentプロパティを使用する必要があります。詳細は後述の「TagHelperContentを使ったHTML編集」でご覧ください。
  • メイン出力要素の要素名や属性を編集するサンプルを次に示します。
    なお、出力例はブラウザのページソースを記載しており、見やすいよう表示順番を変更しています。(開発者モードF12だと実ソースとの違いが大きいため。)
  • TagHelperOutputのTagMode(TagMode列挙体)を指定して、メイン出力要素の開始タグ・終了タグの出力有無を変更できます。
    TagMode値説明備考
    SelfClosing自己完結するタグを出力“<div />”, “<br />”等
    StartTagAndEndTag開始タグ・終了タグを出力“<div></div>”等
    StartTagOnly開始タグのみ出力“<input>”等

    TagHelperのTagModeの既定値はStartTagAndEndTagになっており、開始タグ・終了タグが出力されます。input要素を扱うInputTagHelperのTagModeの既定値はStartTagOnlyになっており終了タグを出力しません。(HTML仕様上、input要素は終了タグが不要であるため、このような既定値になっていると思われます。)

  • なお、TagHelperOutputの属性値(output.Attributes)は、入力検証等のレンダリング処理過程でページソース(cshtml)と差異が発生する場合があります。例えば、サーバ側入力検証が失敗した場合は”input-validation-error”等のCSSクラスが追加される場合があります。そのため、cshtmlで定義した属性のみある前提の実装だと不具合が発生する可能性があります。
  • output.Attributesに含まれるclass属性の値は通常は文字列なのですが、サーバ側入力検証失敗時はClassAttributeHtmlContentオブジェクトが設定されます。この属性(オブジェクト)を別のタグ等にマージやコピーを試みると、class属性の値が”ClassAttributeHtmlContent”のようなクラス名になってしまいます。これは、ClassAttributeHtmlContentがTagHelperOutputExtensionsクラスのprivateクラスであることに起因しているようです。デバッガで当該オブジェクト内部でcssクラス名が保持されていることは確認できるのですが、その値を取得できません。
    回避策として、リフレクションで無理やり内部にアクセスする方法もありますが、context(TagHelperContext)のAllAttributesプロパティに含まれるclass属性値(cshtmlで定義した値)を使用する方法もあります。

TagHelperContentを使ったHTML編集

  • メイン出力要素の前後のHTML要素や子のHTML要素を編集する場合、TagHelperOutputのPreElement, PostElement, PreContent, Content, PostContentプロパティを使用します。
  • これらのプロパティは全てTagHelperContent型なので、ここではTagHhelperContentを使用したHTML編集方法を説明します。
  • TagHelperContentの基本的な操作例を次に示します。
    TagHelerContentにHTML要素を設定(上書き)する場合はSet系メソッド、追加する場合はAppend系メソッドを使用します。”Html”が付かないメソッドは、引数の文字列をHTMLエンコードして設定・追加します。

    • SetContent(), Append()等のようにメソッド名にHtmlがないメソッドは、引数の文字列は「HTMLエンコードされていない文字列」とみなし、HTMLエンコードしてから設定・追加します。ユーザやDBからのデータを使用する場合など、”< >”等のようにHTMLで特別な意味を持つ文字が含まれる可能性がある場合に使用します。(画面崩れの防止やXSS対策のため。)
    • SetHtmlContent(), AppendHtml()等のようにHtmlがあるメソッドは、引数の文字列が「HTMLエンコード済みである」とみなし、HTMLエンコードは行わずにそのまま設定・追加します。プログラマが明示的にタグを編集する(ユーザやDBからの入力等に依存しない)場合は、前述のような危険性はないので、こちらを使用します。
  • TagBuilderの基本的な操作例を次に示します。
    TagBuilderの子としてHTML要素を設定(上書き)する場合はInnerHtmlプロパティに対してSet系メソッド、追加する場合はAppend系メソッドを使用します。”Html”が付かないメソッドは、引数の文字列をHTMLエンコードして設定・追加します。

    • InnerHtmlプロパティのSetContent(), SetHtmlContent()は拡張メソッドのため、”Microsoft.AspNetCore.Html”のusing宣言が必要です。
    • 属性操作に関して、TagHelperOutputと微妙に異なるので混乱しないよう注意が必要です。(TagHelperOutputは文字列またはTagHelperAttributeによる属性操作が可能ですが、TagHelperContentは文字列のみになります。)

メイン出力要素に子要素を追加するサンプル

メイン出力要素に子のHTML要素を追加するタグヘルパーの入出力とサンプルコードを説明します。
Contentに対して子となるHTML要素を追加することで実現します。

メイン出力要素を子要素にするサンプル

メイン出力要素を子要素(ネスト)にするタグヘルパーの入出力とサンプルコードを説明します。
PreElementには終了タグがない要素を追加し、PostElementに終了タグのみを出力することでネストを実現しています。