ASP.NET Core のタグ ヘルパー コンポーネント

作成者: Scott AddieFiyaz Bin Hasan

タグ ヘルパー コンポーネントは、サーバー側のコードから HTML 要素を、条件に応じて変更または追加できるタグ ヘルパーです。 この機能は、ASP.NET Core 2.0 以降で使用できます。

ASP.NET Core には、組み込みのタグ ヘルパー コンポーネントが 2 つ (headbody) 含まれています。 これらは Microsoft.AspNetCore.Mvc.Razor.TagHelpers 名前空間に配置され、MVC と Razor Pages の両方で使用できます。 タグ ヘルパー コンポーネントには、_ViewImports.cshtml でのアプリへの登録は必要ありません。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

ユース ケース

タグ ヘルパー コンポーネントの 2 つの一般的なユース ケースは、次のとおりです。

  1. <link><head> に挿入する
  2. <script><body> に挿入する

次のセクションでは、これらのユース ケースについて説明します。

HTML の head 要素の挿入

HTML <head> 要素内で、CSS ファイルは HTML <link> 要素でよくインポートされます。 次のコードでは、head タグ ヘルパー コンポーネントを使用して <link> 要素が <head> 要素に挿入されます。

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace RazorPagesSample.TagHelpers
{
    public class AddressStyleTagHelperComponent : TagHelperComponent
    {
        private readonly string _style = 
            @"<link rel=""stylesheet"" href=""/css/address.css"" />";

        public override int Order => 1;

        public override Task ProcessAsync(TagHelperContext context,
                                          TagHelperOutput output)
        {
            if (string.Equals(context.TagName, "head", 
                              StringComparison.OrdinalIgnoreCase))
            {
                output.PostContent.AppendHtml(_style);
            }

            return Task.CompletedTask;
        }
    }
}

上のコードでは以下の操作が行われます。

  • AddressStyleTagHelperComponent は、TagHelperComponent を実装します。 抽象化では次のことを行います。
    • TagHelperContext を使ってクラスの初期化を許可。
    • タグ ヘルパー コンポーネントの使用を有効にして、HTML 要素を追加または変更。
  • Order プロパティでは、コンポーネントがレンダリングされる順序を定義します。 アプリでタグ ヘルパー コンポーネントが複数使用されている場合、Order が必要です。
  • ProcessAsync では、実行コンテキストの TagName プロパティ値が head と比較されます。 比較評価の結果が true の場合は、_style フィールドのコンテンツが HTML <head> 要素に挿入されます。

HTML の body 要素に挿入

body タグ ヘルパー コンポーネントによって、<script> 要素を <body> 要素に挿入できます。 次のコードはこの技法を示しています。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace RazorPagesSample.TagHelpers
{
    public class AddressScriptTagHelperComponent : TagHelperComponent
    {
        public override int Order => 2;
        
        public override async Task ProcessAsync(TagHelperContext context,
                                                TagHelperOutput output)
        {
            if (string.Equals(context.TagName, "body",
                              StringComparison.OrdinalIgnoreCase))
            {
                var script = await File.ReadAllTextAsync(
                    "TagHelpers/Templates/AddressToolTipScript.html");
                output.PostContent.AppendHtml(script);
            }
        }
    }
}

個別の HTML ファイルを使用して、<script> 要素を格納します。 HTML ファイルを使用すると、コードはより整理され、よりメンテナンスしやすくなります。 上のコードでは、TagHelpers/Templates/AddressToolTipScript.html のコンテンツを読み取り、タグ ヘルパーの出力でそれを追加します。 AddressToolTipScript.html ファイルには、次のマークアップが含まれます。

<script>
$("address[printable]").hover(function() {
    $(this).attr({
        "data-toggle": "tooltip",
        "data-placement": "right",
        "title": "Home of Microsoft!"
    });
});
</script>

上のコードでは、ブートストラップ ヒント ウィジェットprintable 属性を含む任意の <address> 要素にバインドします。 要素の上にマウス ポインターが移動したときに、効果が表示されます。

コンポーネントの登録

タグ ヘルパー コンポーネントは、アプリのタグ ヘルパー コンポーネント コレクションに追加する必要があります。 コレクションに追加するには、次の 3 つの方法があります。

サービス コンテナーによる登録

タグ ヘルパー コンポーネント クラスが ITagHelperComponentManager で管理されていない場合、依存関係挿入 (DI) システムで登録する必要があります。 次の Startup.ConfigureServices コードでは、一時的な有効期間AddressStyleTagHelperComponent クラスと AddressScriptTagHelperComponent クラスを登録します。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddTransient<ITagHelperComponent, 
        AddressScriptTagHelperComponent>();
    services.AddTransient<ITagHelperComponent, 
        AddressStyleTagHelperComponent>();
}

Razor ファイルによる登録

タグ ヘルパー コンポーネントが DI で登録されていない場合は、Razor Pages ページまたは MVC ビューから登録できます。 この技法は、Razor ファイルから挿入されたマークアップとコンポーネントの実行する順番を制御するために使用されます。

ITagHelperComponentManager を使用して、タグ ヘルパー コンポーネントを追加したり、アプリから削除したりします。 次のコードでは、AddressTagHelperComponent を使ってこの技法を示します。

@using RazorPagesSample.TagHelpers;
@using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
@inject ITagHelperComponentManager manager;

@{
    string markup;

    if (Model.IsWeekend)
    {
        markup = "<em class='text-warning'>Office closed today!</em>";
    }
    else
    {
        markup = "<em class='text-info'>Office open today!</em>";
    }

    manager.Components.Add(new AddressTagHelperComponent(markup, 1));
}

上のコードでは以下の操作が行われます。

  • @inject ディレクティブでは、ITagHelperComponentManager のインスタンスが提供されます。 インスタンスは、Razor ファイルでダウンストリームのアクセスを行うために、manager という名前の変数に割り当てられます。
  • AddressTagHelperComponent のインスタンスは、アプリのタグ ヘルパー コンポーネント コレクションに追加されます。

AddressTagHelperComponent は、markuporder パラメーターを受け入れるコンストラクターに反映するために変更されます。

private readonly string _markup;

public override int Order { get; }

public AddressTagHelperComponent(string markup = "", int order = 1)
{
    _markup = markup;
    Order = order;
}

指定された markup パラメーターは、次のように ProcessAsync で使用されます。

public override async Task ProcessAsync(TagHelperContext context,
                                        TagHelperOutput output)
{
    if (string.Equals(context.TagName, "address",
            StringComparison.OrdinalIgnoreCase) &&
        output.Attributes.ContainsName("printable"))
    {
        TagHelperContent childContent = await output.GetChildContentAsync();
        string content = childContent.GetContent();
        output.Content.SetHtmlContent(
            $"<div>{content}<br>{_markup}</div>{_printableButton}");
    }
}

ページ モデルまたはコントローラーによる登録

タグ ヘルパー コンポーネントが DI で登録されていない場合は、Razor Pages ページ モデルまたは MVC コントローラーから登録できます。 この技法は、Razor ファイルから C# ロジックを分離するのに便利です。

コンストラクター挿入を使用して、ITagHelperComponentManager のインスタンスにアクセスします。 タグ ヘルパー コンポーネントは、インスタンスのタグ ヘルパー コンポーネント コレクションに追加されます。 次の Razor Pages ページ モデルでは、AddressTagHelperComponent を使ったこの技法を示します。

using System;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesSample.TagHelpers;

public class IndexModel : PageModel
{
    private readonly ITagHelperComponentManager _tagHelperComponentManager;

    public bool IsWeekend
    {
        get
        {
            var dayOfWeek = DateTime.Now.DayOfWeek;

            return dayOfWeek == DayOfWeek.Saturday ||
                   dayOfWeek == DayOfWeek.Sunday;
        }
    }

    public IndexModel(ITagHelperComponentManager tagHelperComponentManager)
    {
        _tagHelperComponentManager = tagHelperComponentManager;
    }

    public void OnGet()
    {
        string markup;

        if (IsWeekend)
        {
            markup = "<em class='text-warning'>Office closed today!</em>";
        }
        else
        {
            markup = "<em class='text-info'>Office open today!</em>";
        }

        _tagHelperComponentManager.Components.Add(
            new AddressTagHelperComponent(markup, 1));
    }
}

上のコードでは以下の操作が行われます。

  • コンストラクター挿入を使用して、ITagHelperComponentManager のインスタンスにアクセスします。
  • AddressTagHelperComponent のインスタンスは、アプリのタグ ヘルパー コンポーネント コレクションに追加されます。

コンポーネントの作成

カスタムのタグ ヘルパー コンポーネントを作成するには

次のコードでは、<address> HTML 要素をターゲットとするカスタムのタグ ヘルパー コンポーネントが作成されます。

using System.ComponentModel;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;

namespace RazorPagesSample.TagHelpers
{
    [HtmlTargetElement("address")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class AddressTagHelperComponentTagHelper : TagHelperComponentTagHelper
    {
        public AddressTagHelperComponentTagHelper(
            ITagHelperComponentManager componentManager, 
            ILoggerFactory loggerFactory) : base(componentManager, loggerFactory)
        {
        }
    }
}

カスタムの address タグ ヘルパー コンポーネントを使用して、次のように HTML マークアップを挿入します。

public class AddressTagHelperComponent : TagHelperComponent
{
    private readonly string _printableButton =
        "<button type='button' class='btn btn-info' onclick=\"window.open(" +
        "'https://binged.it/2AXRRYw')\">" +
        "<span class='glyphicon glyphicon-road' aria-hidden='true'></span>" +
        "</button>";

    public override int Order => 3;

    public override async Task ProcessAsync(TagHelperContext context,
                                            TagHelperOutput output)
    {
        if (string.Equals(context.TagName, "address",
                StringComparison.OrdinalIgnoreCase) &&
            output.Attributes.ContainsName("printable"))
        {
            var content = await output.GetChildContentAsync();
            output.Content.SetHtmlContent(
                $"<div>{content.GetContent()}</div>{_printableButton}");
        }
    }
}

上の ProcessAsync メソッドでは、SetHtmlContent に提供された HTML が一致する <address> 要素に挿入されます。 挿入は次の場合に発生します。

  • 実行コンテキストの TagName プロパティ値が address と等しい場合。
  • 対応する <address> 要素に printable 属性がある場合。

たとえば、次の <address> 要素を処理しているときに、if ステートメントの評価の結果が true になります。

<address printable>
    One Microsoft Way<br />
    Redmond, WA 98052-6399<br />
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

その他のリソース