しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET Core MVC 1.0 で追加された View Components を使ってみる

ASP.NET Core MVC 1.0 に追加された機能の中で 1,2 を争うぐらい個人的には気に入ってるのが View Components です。ざっくりと説明すると Razor の中だけで使える Controller/Action です。

MVC 5 までは Html.Action/RenderAction を使って呼び出せる、子アクションという概念がありましたが、完全に切り出されたのが View Components となります。

View components in ASP.NET Core | Microsoft Learn

Visual Studio にテンプレートが用意されていないので、今は以下のような規約のクラスを作成する必要があります。コントローラの規約に近いような形になってます。

  • ViewComponent クラスを継承
  • ViewComponent で終わるクラス名、もしくは ViewComponent 属性で名前を指定
  • Invoke / InvokeAsync メソッドを実装

実際に View Components を作ってみて確認しておきます。よくある Hello, World です。RC 2 では仕様が変わっているので、注意したいところです。

View Component を作る

ViewComponent クラスを継承し、同期処理の場合は Invoke メソッド、非同期処理の場合は InvokeAsync メソッドを実装すると、それだけで View Components が完成します。

public class SampleViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(string message)
    {
        ViewData["message"] = message;

        return View();
    }
}

このクラスの場合は Sample という名前で呼び出すことが出来ますが、ViewComponent 属性を使って別名を付けることも出来ます。

当然ながら View Component も DI の対象なので、コンストラクタで様々なインスタンスを取得できるようになっています。各種サービスクラスを使った処理を簡単に書けます。

public class SampleViewComponent : ViewComponent
{
    public SampleViewComponent(DbContext context)
    {
        _context = context;
    }

    private DbContext _context;

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var list = await _context.Products.ToListAsync();

        return View(list);
    }
}

データベースから情報を取得してコンテンツを返す View Components が便利です。公式ドキュメントでもタグクラウドやサイドバーのコンテンツなどといった用途が紹介されています。

ビューを用意する

View Components は Razor を使ってコンテンツを返すことが出来るので、その場合は Components/[Name]/Default.cshtml というファイルを用意しておきます。

今回は Shared に置いてありますが、コントローラ名以下に置くことも出来ます。呼び出し元によってビューを切り替えることが出来るので、柔軟性が高いです。

ビューから呼び出す

今回の RC 2 で Component.Invoke メソッドが削除されたため、Razor では @await を使って InvokeAsync メソッドで実行する必要があります。

<p>
    @await Component.InvokeAsync("Sample", new { message = "Hello, World" })
<p>

引数には匿名クラスで View Components に渡すデータを指定します。RC 2 で変更された部分です。

Component には InvokeAsync<T> というジェネリックなメソッドも用意されているので、文字列ベースで指定したくない場合はこっちを使いましょう。実行時エラーになるのを避けることが出来ます。

<p>
    @(await Component.InvokeAsync<SampleViewComponent>(new { message = "Hello, World" }))
<p>

今は ReSharper が新しい Razor に対応していないですが、対応した暁にはリファクタリング機能でクラス名変更とかに簡単に対応できるはずです。

View Components を使うと、これまで実装で悩ましかったページで共通に使われる動的なコンテンツの扱いが大変楽になるので、積極的に使っていきたいですね。