ASP.NET Core のビュー コンポーネント

作成者: Rick Anderson

ビュー コンポーネント

ビュー コンポーネントは部分ビューと似ていますが、はるかに強力なものです。 ビュー コンポーネントはモデル バインドを使用せず、ビュー コンポーネントを呼び出すときに渡されるデータに依存します。 この記事はコントローラーとビューを使用して記述されましたが、ビュー コンポーネントは Pages で Razor 動作します

ビュー コンポーネントの特徴は次のとおりです。

  • 応答全体ではなく、チャンクをレンダリングします。
  • コントローラーとビューの間にあるのと同じ関心の分離とテストの容易性の利点があります。
  • パラメーターとビジネス ロジックを含めることができます。
  • 通常、レイアウト ページから呼び出されます。

ビュー コンポーネントは、次のような部分ビューに対して複雑すぎる再利用可能なレンダリング ロジックを目的としています。

  • 動的なナビゲーション メニュー
  • データベースにクエリを実行するクラウドにタグを付ける
  • サインイン パネル
  • ショッピング カート
  • 新着情報の記事
  • ブログのサイドバー コンテンツ
  • ユーザーのサインイン状態に応じて、すべてのページに表示され、サインアウトまたはサインインするリンクが表示されるサインイン パネル

ビュー コンポーネントは、次の 2 つの部分で構成されます。

  • このクラスは、通常、次から派生します。 ViewComponent
  • 返される結果 (通常はビュー)。

コントローラーと同様に、ビュー コンポーネントは POCO にすることができますが、ほとんどの開発者は、派生 ViewComponentして使用できるメソッドとプロパティを利用します。

ビュー コンポーネントが、アプリの仕様を満たしているかどうかを検討する場合は、代わりに Razor コンポーネントを使用することを検討してください。 Razor コンポーネントでも、C# コードとマークアップを組み合わせて、再利用可能な UI ユニットを生成できます。 Razor コンポーネントは、クライアント側で UI ロジックと構成を提供する場合の開発者の生産性のために設計されています。 詳細については、「ASP.NET Core Razor コンポーネント」を参照してください。 MVC または Pages アプリにコンポーネントを組み込むRazor方法については、「Prerender と ASP.NET Core コンポーネントのRazor統合」を参照してください。Razor

ビュー コンポーネントを作成する

このセクションには、ビュー コンポーネントを作成するための高レベルの要件が含まれています。 記事の後半で、各ステップの詳細を検証し、ビュー コンポーネントを作成します。

ビュー コンポーネント クラス

ビュー コンポーネント クラスは、次のいずれかによって作成できます。

  • 派生 ViewComponent
  • [ViewComponent] 属性でクラスを装飾するか、[ViewComponent] 属性でクラスから派生させる
  • 名前がサフィックスで終わるクラスの作成 ViewComponent

コントローラーと同様に、ビュー コンポーネントは、パブリック クラス、入れ子にされていないクラス、および非抽象クラスである必要があります。 ビュー コンポーネント名は、サフィックスが削除された ViewComponent クラス名です。 また、これは Name プロパティを使用して、明示的に指定することもできます。

ビュー コンポーネント クラスの特徴は次のとおりです。

  • コンストラクターの依存関係の挿入をサポートします
  • コントローラーのライフサイクルに参加しないため、ビュー コンポーネントで フィルター を使用することはできません

大文字と小文字を区別しない ViewComponent サフィックスを持つクラスがビュー コンポーネントとして扱われないようにするには、属性を使用してクラスを [NonViewComponent] 装飾します。

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

ビュー コンポーネント メソッド

ビュー コンポーネントでは、次のロジックが定義されます。

  • InvokeAsync を返す Task<IViewComponentResult>メソッドです。
  • Invoke を返す IViewComponentResult同期メソッドです。

パラメーターは、モデル バインドではなく、ビュー コンポーネントから直接取得します。 ビュー コンポーネントが要求を直接処理することはありません。 通常、ビュー コンポーネントは、モデルを初期化し、View メソッドを呼び出してビューに渡します。 要約すると、ビュー コンポーネント メソッドの特徴は次のとおりです。

  • Task<IViewComponentResult> を返す InvokeAsync メソッドまたは IViewComponentResult を返す同期 Invoke メソッドを定義します。
  • 通常、モデルを初期化し、 ViewComponent.View メソッドを呼び出してビューに渡します。
  • パラメーターは HTTP ではなく、呼び出し元のメソッドから取得されます。 モデル バインドはありません。
  • HTTP エンドポイントとして直接到達できません。 通常、これらはビューで呼び出されます。 ビュー コンポーネントでは要求が処理されません。
  • 現在の HTTP 要求からの詳細ではなく、シグネチャでオーバーロードされます。

ビューの検索パス

ランタイムでは、次のパスでビューを検索します。

  • /Views/{コントローラー名}/Components/{ビュー コンポーネント名}/{ビュー名}
  • /Views/Shared/Components/{ビュー コンポーネント名}/{ビュー名}
  • /Pages/Shared/Components/{ビュー コンポーネント名}/{ビュー名}

この検索パスは、コントローラーとビューおよび Razor Pages を使うプロジェクト用です。

ビュー コンポーネントの既定のビュー名は Default、ビュー ファイルの名前 Default.cshtmlが通常であることを意味します。 ビュー コンポーネントの結果を作成するとき、またはメソッドを呼び出すときに、別のビュー名を View 指定できます。

ビュー ファイル Default.cshtml に名前を付け、 Views/Shared/Components/{View Component Name}/{View Name} パスを使用することをお勧めします。 このサンプルで使用するビュー コンポーネントは PriorityList 、ビュー コンポーネント ビューに使用 Views/Shared/Components/PriorityList/Default.cshtml します。

ビューの検索パスをカスタマイズする

ビューの検索パスをカスタマイズするには、Razor の ViewLocationFormats コレクションを変更します。 たとえば、パス /Components/{View Component Name}/{View Name}内のビューを検索するには、コレクションに新しい項目を追加します。

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

前のコードでは、プレースホルダー {0} はパス Components/{View Component Name}/{View Name}を表します。

ビュー コンポーネントを呼び出す

ビュー コンポーネントを使用するには、ビュー内で以下を呼び出します。

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

パラメーターはメソッドに InvokeAsync 渡されます。 アーティ PriorityList クルで開発されたビュー コンポーネントは、ビュー ファイルから Views/ToDo/Index.cshtml 呼び出されます。 次のコードでは、メソッドは InvokeAsync 2 つのパラメーターを使用して呼び出されます。

</table>

<div>
    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

ビュー コンポーネントをタグ ヘルパーとして呼び出す

ビュー コンポーネントは、 タグ ヘルパーとして呼び出すことができます。

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

タグ ヘルパーのパスカル ケースのクラスとメソッドのパラメーターは、それぞれケバブ ケースに変換されます。 ビュー コンポーネントを呼び出すタグ ヘルパーでは、<vc></vc> 要素を使用します。 ビュー コンポーネントは、次のように指定されます。

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

ビュー コンポーネントをタグ ヘルパーとして使用するには、@addTagHelper ディレクティブを使用して、ビュー コンポーネントを含むアセンブリを登録します。 ビュー コンポーネントが呼び出された MyWebAppアセンブリ内にある場合は、次のディレクティブをファイルに _ViewImports.cshtml 追加します。

@addTagHelper *, MyWebApp

ビュー コンポーネントは、ビュー コンポーネントを参照する任意のファイルにタグ ヘルパーとして登録できます。 タグ ヘルパーを登録する方法の詳細については、「Managing Tag Helper Scope」 (タグ ヘルパーのスコープの管理) を参照してください。

このチュートリアルで使用される InvokeAsync メソッドは、次のとおりです。

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

上記のマークアップでは、ビュー コンポーネントは PriorityListpriority-list. ビュー コンポーネントに対するパラメーターは、ケバブ ケースの属性として渡されます。

コントローラーからビュー コンポーネントを直接呼び出す

ビュー コンポーネントは通常、ビューから呼び出されますが、コントローラー メソッドから直接呼び出すことができます。 ビュー コンポーネントはコントローラーなどのエンドポイントを定義しませんが、コンテンツを返すコントローラー アクションを ViewComponentResult 実装できます。

次の例では、ビュー コンポーネントはコントローラーから直接呼び出されます。

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

基本的なビュー コンポーネントを作成する

スタート コードをダウンロード、ビルド、およびテストします。 これは、ToDo 項目の一覧をToDo表示するコントローラーを備えた基本的なプロジェクトです。

[ToDo] のリスト

優先度と完了状態を渡すようにコントローラーを更新する

優先度と完了の Index 状態パラメーターを使用するようにメソッドを更新します。

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

ViewComponent クラスの追加

ViewComponent クラスを次に追加します ViewComponents/PriorityListViewComponent.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

    public async Task<IViewComponentResult> InvokeAsync(
                                            int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

コードに関する注意事項

  • ビュー コンポーネント クラスは、プロジェクト内の任意のフォルダーに含めることができます。

  • クラス名 PriorityListViewComponent はサフィックス ViewComponent で終わるため、ランタイムはビューからクラス コンポーネントを参照するときに文字列 PriorityList を使用します。

  • [ViewComponent] 属性では、ビュー コンポーネントを参照するために使用する名前を変更できます。 たとえば、次の属性を使用してクラスに名前 XYZ[ViewComponent] 付けたとします。

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • 前のコードの属性は [ViewComponent] 、ビュー コンポーネント セレクターに使用するように指示します。

    • コンポーネントに関連付けられているビューを探すときの名前PriorityList
    • ビューからクラス コンポーネントを参照するときの文字列 "PriorityList" です。
  • コンポーネントでは、依存性の注入を使用して、データ コンテキストを利用できるようにします。

  • InvokeAsync はビューから呼び出すことができるメソッドを公開し、任意の数の引数を受け取ることができます。

  • InvokeAsync メソッドでは、isDonemaxPriority パラメーターを満たす ToDo 項目のセットを返します。

ビュー コンポーネント Razor ビューの作成

  • Views/Shared/Components フォルダーを作成します。 このフォルダーは、Components という名前にする必要があります

  • Views/Shared/Components/PriorityList フォルダーを作成します。 このフォルダー名は、ビュー コンポーネント クラスの名前、またはクラスの名前からサフィックスを引いた名前と一致する必要があります。 属性を使用する ViewComponent 場合、クラス名は属性の指定と一致する必要があります。

  • ビューを作成します Views/Shared/Components/PriorityList/Default.cshtmlRazor 。

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    Razor ビューでは、TodoItem のリストを取得してそれらを表示します。 ビュー コンポーネント InvokeAsync メソッドがビューの名前を渡さない場合は、規則によってビュー名に Default が使用されます。 特定のコントローラーの既定のスタイルをオーバーライドするには、コントローラーに固有のビュー フォルダー (例: Views/ToDo/Components/PriorityList/Default.cshtml) にビューを追加します。

    ビュー コンポーネントがコントローラー固有の場合は、コントローラー固有のフォルダーに追加できます。 たとえば、 Views/ToDo/Components/PriorityList/Default.cshtml コントローラー固有です。

  • div優先度リスト コンポーネントを含む呼び出しをファイルの下部にViews/ToDo/index.cshtml追加します。

    </table>
    
    <div>
        Maxium Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

@await Component.InvokeAsync マークアップは、ビュー コンポーネントを呼び出すための構文を示します。 最初の引数は、呼び出す必要があるコンポーネントの名前です。 後続のパラメーターは、そのコンポーネントに渡されます。 InvokeAsync では、任意の数の引数を取得できます。

アプリをテストします。 次の画像は、[ToDo] リストと優先順位の項目を示します。

[ToDo] リストと優先順位の項目

ビュー コンポーネントは、コントローラーから直接呼び出すことができます。

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

IndexVC アクションからの優先順位の項目

ビュー コンポーネント名を指定する

複雑なビュー コンポーネントでは、いくつかの条件下で、既定以外のビューを指定する必要がある場合があります。 次のコードは、メソッドから "PVC" ビューを指定する方法を InvokeAsync 示しています。 PriorityListViewComponent クラスで InvokeAsync メソッドを更新します。

public async Task<IViewComponentResult> InvokeAsync(
                                           int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

ファイルを Views/Shared/Components/PriorityList/Default.cshtml 名前付きの Views/Shared/Components/PriorityList/PVC.cshtmlビューにコピーします。 PVC ビューが使用されていることを示すために、見出しを追加します。

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

アプリを実行して、PVC ビューを確認します。

優先順位のビュー コンポーネント

PVC ビューがレンダリングされない場合は、優先度が 4 以上のビュー コンポーネントが呼び出されていることを確認します。

ビューのパスを調べる

  • 優先順位ビューが返されないように、優先順位パラメーターを 3 以下に変更します。

  • に一時的に名前を変更しますViews/ToDo/Components/PriorityList/Default.cshtml1Default.cshtml

  • アプリをテストすると、次のエラーが発生します。

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Views/ToDo/Components/PriorityList/1Default.cshtmlViews/Shared/Components/PriorityList/Default.cshtml にコピーします。

  • [Shared] の [ToDo] ビュー コンポーネントのビューにマークアップを追加して、そのビューが [Shared] フォルダーからのものであることを示します。

  • [Shared] コンポーネント ビューをテストします。

[Shared] コンポーネント ビューを含む [ToDo] 出力

ハードコーディングされた文字列を避ける

コンパイル時の安全性を確保するために、ハードコーディングされたビュー コンポーネント名をクラス名に置き換えます。 PriorityListViewComponent.cs ファイルを更新して、"ViewComponent" サフィックスを使用しないようにします。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(
                                               int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

ビュー ファイル:

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

CLR 型を受け取るメソッドの Component.InvokeAsync オーバーロードでは、演算子を typeof 使用します。

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

同期作業を実行する

非同期処理が必要ない場合、フレームワークは同期 Invoke メソッドの呼び出しを処理します。 次のメソッドでは同期 Invoke ビュー コンポーネントを作成します。

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

ビュー コンポーネントの Razor ファイル:

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

ビュー コンポーネントは、次のいずれかの方法を Razor 使用してファイル (たとえば Views/Home/Index.cshtml) で呼び出されます。

IViewComponentHelper の方法を使用するには、Component.InvokeAsync を呼び出します。

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

タグ ヘルパーを使用するには、@addTagHelper ディレクティブを使用して、ビュー コンポーネントを含むアセンブリを登録します (ビュー コンポーネントは、MyWebApp と呼ばれるアセンブリ内にあります)。

@addTagHelper *, MyWebApp

Razor マークアップ ファイルでビュー コンポーネントのタグ ヘルパーを使用します。

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

PriorityList.Invoke のメソッド署名は同期的ですが、Razor ではマークアップ ファイルで Component.InvokeAsync を使用してメソッドを見つけて呼び出します。

その他の技術情報

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

ビュー コンポーネント

ビュー コンポーネントは部分ビューと似ていますが、はるかに強力なものです。 ビュー コンポーネントでは、モデル バインドを使用せず、呼び出すときに指定されたデータのみに依存します。 この記事はコントローラーとビューを使用して作成されましたが、ビュー コンポーネントは Razor Pages でも利用できます。

ビュー コンポーネントの特徴は次のとおりです。

  • 応答全体ではなく、チャンクをレンダリングします。
  • コントローラーとビューの間にあるのと同じ関心の分離とテストの容易性の利点があります。
  • パラメーターとビジネス ロジックを含めることができます。
  • 通常、レイアウト ページから呼び出されます。

ビュー コンポーネントは、次のような部分ビューには複雑すぎる、再利用可能なレンダリング ロジックをどこでも使用できるようにするためのものです。

  • 動的なナビゲーション メニュー
  • タグ クラウド (データベースをクエリする場所)
  • ログイン パネル
  • ショッピング カート
  • 新着情報の記事
  • 一般的なブログのサイドバーのコンテンツ
  • すべてのページでレンダリングされ、ユーザーの状態のログに応じて、ログアウトまたはログインのいずれかのリンクを示すログイン パネル

ビュー コンポーネントは、クラス (通常は派生) と返される ViewComponent結果 (通常はビュー) の 2 つの部分で構成されます。 コントローラーと同様に、ビュー コンポーネントは POCO にすることができますが、ほとんどの開発者は、派生 ViewComponentして使用できるメソッドとプロパティを利用します。

ビュー コンポーネントが、アプリの仕様を満たしているかどうかを検討する場合は、代わりに Razor コンポーネントを使用することを検討してください。 Razor コンポーネントでも、C# コードとマークアップを組み合わせて、再利用可能な UI ユニットを生成できます。 Razor コンポーネントは、クライアント側で UI ロジックと構成を提供する場合の開発者の生産性のために設計されています。 詳細については、「ASP.NET Core Razor コンポーネント」を参照してください。 MVC または Pages アプリにコンポーネントを組み込むRazor方法については、「Prerender と ASP.NET Core コンポーネントのRazor統合」を参照してください。Razor

ビューのコンポーネントを作成する

このセクションには、ビュー コンポーネントを作成するための高レベルの要件が含まれています。 記事の後半で、各ステップの詳細を検証し、ビュー コンポーネントを作成します。

ビュー コンポーネント クラス

ビュー コンポーネント クラスは、次のいずれかによって作成できます。

  • ViewComponent から派生させる
  • [ViewComponent] 属性でクラスを装飾するか、[ViewComponent] 属性でクラスから派生させる
  • 名前がサフィックス ViewComponent で終わるクラスを作成する

コントローラーと同様に、ビュー コンポーネントは、パブリック クラス、入れ子にされていないクラス、および非抽象クラスである必要があります。 ビュー コンポーネント名は、"ViewComponent" サフィックスを除いたクラス名です。 また、これは ViewComponentAttribute.Name プロパティを使用して、明示的に指定することもできます。

ビュー コンポーネント クラスの特徴は次のとおりです。

  • コンストラクターの依存性の注入を完全にサポートします
  • コントローラーのライフサイクルに関わりません。つまり、ビュー コンポーネントでフィルターを使用できないということです

大文字と小文字を区別しない ViewComponent サフィックスを持つクラスがビュー コンポーネントとして処理されないようにするには、クラスを [NonViewComponent] 属性で装飾します。

[NonViewComponent]
public class ReviewComponent
{
    // ...

ビュー コンポーネント メソッド

ビュー コンポーネントでは、Task<IViewComponentResult> を返す InvokeAsync メソッドまたは IViewComponentResult を返す同期 Invoke メソッドでロジックを定義します。 パラメーターは、モデル バインドではなく、ビュー コンポーネントから直接取得します。 ビュー コンポーネントが要求を直接処理することはありません。 通常、ビュー コンポーネントは、モデルを初期化し、View メソッドを呼び出してビューに渡します。 要約すると、ビュー コンポーネント メソッドの特徴は次のとおりです。

  • Task<IViewComponentResult> を返す InvokeAsync メソッドまたは IViewComponentResult を返す同期 Invoke メソッドを定義します。
  • 通常、モデルを初期化し、ViewComponentView メソッドを呼び出してビューに渡します。
  • パラメーターは HTTP ではなく、呼び出し元のメソッドから取得されます。 モデル バインドはありません。
  • HTTP エンドポイントとして直接到達することはできません。 (通常はビュー内で) コードから呼び出されます。 ビュー コンポーネントでは要求が処理されません。
  • 現在の HTTP 要求からの詳細ではなく、シグネチャでオーバーロードされます。

ビューの検索パス

ランタイムでは、次のパスでビューを検索します。

  • /Views/{コントローラー名}/Components/{ビュー コンポーネント名}/{ビュー名}
  • /Views/Shared/Components/{ビュー コンポーネント名}/{ビュー名}
  • /Pages/Shared/Components/{ビュー コンポーネント名}/{ビュー名}

この検索パスは、コントローラーとビューおよび Razor Pages を使うプロジェクト用です。

ビュー コンポーネントの既定のビュー名は Default です。つまり、ビュー ファイルには通常、名前が付けられます Default.cshtml。 ビュー コンポーネントの結果を作成したり、View メソッドを呼び出したりするときに、別のビュー名を指定することができます。

ビュー ファイル Default.cshtml に名前を付け、 Views/Shared/Components/{View Component Name}/{View Name} パスを使用することをお勧めします。 このサンプルで使用するビュー コンポーネントは PriorityList 、ビュー コンポーネント ビューに使用 Views/Shared/Components/PriorityList/Default.cshtml します。

ビューの検索パスをカスタマイズする

ビューの検索パスをカスタマイズするには、Razor の ViewLocationFormats コレクションを変更します。 たとえば、"/Components/{ビュー コンポーネント名}/{ビュー名}" というパス内のビューを検索するには、次のように新しい項目をコレクションに追加します。

services.AddMvc()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

上記のコードでは、プレースホルダー "{0}" はパス "/Components/{ビュー コンポーネント名}/{ビュー名}" を表しています。

ビュー コンポーネントを呼び出す

ビュー コンポーネントを使用するには、ビュー内で以下を呼び出します。

@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters})

パラメーターは、InvokeAsync メソッドに渡されます。 アーティ PriorityList クルで開発されたビュー コンポーネントは、ビュー ファイルから Views/ToDo/Index.cshtml 呼び出されます。 以下では、InvokeAsync メソッドは、2 つのパラメーターで呼び出されます。

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

タグ ヘルパーとしてビュー コンポーネントを呼び出す

ASP.NET Core 1.1 以降の場合は、タグ ヘルパーとしてビュー コンポーネントを呼び出すことができます。

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

タグ ヘルパーのパスカル ケースのクラスとメソッドのパラメーターは、それぞれケバブ ケースに変換されます。 ビュー コンポーネントを呼び出すタグ ヘルパーでは、<vc></vc> 要素を使用します。 ビュー コンポーネントは、次のように指定されます。

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

ビュー コンポーネントをタグ ヘルパーとして使用するには、@addTagHelper ディレクティブを使用して、ビュー コンポーネントを含むアセンブリを登録します。 ビュー コンポーネントが呼び出された MyWebAppアセンブリ内にある場合は、ファイルに次のディレクティブを _ViewImports.cshtml 追加します。

@addTagHelper *, MyWebApp

ビュー コンポーネントを参照する任意のファイルへのタグ ヘルパーとして、ビュー コンポーネントを登録できます。 タグ ヘルパーを登録する方法の詳細については、「Managing Tag Helper Scope」 (タグ ヘルパーのスコープの管理) を参照してください。

このチュートリアルで使用される InvokeAsync メソッドは、次のとおりです。

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

タグ ヘルパーのマークアップでは、次のようになります。

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

上記のサンプルでは、PriorityList ビュー コンポーネントは priority-list になります。 ビュー コンポーネントに対するパラメーターは、ケバブ ケースの属性として渡されます。

ビュー コンポーネントをコントローラーから直接呼び出す

通常、ビュー コンポーネントはビューから呼び出されますが、コントローラー メソッドから直接呼び出すことができます。 ビュー コンポーネントでコントローラーなどのエンドポイントを定義しないときに、ViewComponentResult のコンテンツを返すコントローラー アクションを簡単に実装できます。

この例では、ビュー コンポーネントは、コントローラーから直接呼び出されます。

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

チュートリアル: 単純なビュー コンポーネントの作成

スタート コードをダウンロード、ビルド、およびテストします。 これは、 [ToDo] 項目のリストを表示する ToDo コントローラーを備えた、単純なプロジェクトです。

[ToDo] のリスト

ViewComponent クラスの追加

ViewComponents フォルダーを作成して、次の PriorityListViewComponent クラスを追加します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListViewComponent(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

コードに関する注意事項

  • ビュー コンポーネント クラスは、プロジェクト内の任意のフォルダーに含めることができます。

  • クラス名 PriorityListViewComponent はサフィックス ViewComponent で終わるため、ランタイムはビューからクラス コンポーネントを参照するときに文字列 PriorityList を使用します。

  • [ViewComponent] 属性では、ビュー コンポーネントを参照するために使用する名前を変更できます。 たとえば、次の属性を使用してクラスに名前 XYZViewComponent 付けた可能性があります。

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • 前のコードの属性は [ViewComponent] 、ビュー コンポーネント セレクターに使用を指示します。

    • コンポーネントに関連付けられているビューを検索するときの名前PriorityList
    • ビューからクラス コンポーネントを参照するときの文字列 "PriorityList" です。
  • コンポーネントでは、依存性の注入を使用して、データ コンテキストを利用できるようにします。

  • InvokeAsync ではビューから呼び出すことができるメソッドを表示し、任意の数の引数を取得できます。

  • InvokeAsync メソッドでは、isDonemaxPriority パラメーターを満たす ToDo 項目のセットを返します。

ビュー コンポーネント Razor ビューの作成

  • Views/Shared/Components フォルダーを作成します。 このフォルダーには名前を付けるComponents必要があります

  • Views/Shared/Components/PriorityList フォルダーを作成します。 このフォルダー名は、ビュー コンポーネント クラスの名前、または (規則に従い、クラス名に ViewComponent サフィックスを使用した場合は) サフィックスを差し引いたクラスの名前に一致する必要があります。 ViewComponent 属性を使用した場合は、クラス名は属性の指定に一致する必要があります。

  • ビューを作成します Views/Shared/Components/PriorityList/Default.cshtmlRazor 。

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    Razor ビューでは、TodoItem のリストを取得してそれらを表示します。 ビュー コンポーネントの InvokeAsync メソッドで (サンプルのように) ビューの名前を渡さない場合、Default が規則によってビュー名に使用されます。 このチュートリアルの後半で、ビューの名前を渡す方法について示します。 特定のコントローラーの既定のスタイルをオーバーライドするには、コントローラーに固有のビュー フォルダー (例: Views/ToDo/Components/PriorityList/Default.cshtml) にビューを追加します。

    ビュー コンポーネントがコントローラー固有の場合は、コントローラー固有のフォルダー (Views/ToDo/Components/PriorityList/Default.cshtml) に追加できます。

  • div優先度リスト コンポーネントの呼び出しを含むファイルの下部にViews/ToDo/index.cshtml追加します。

    </table>
    <div>
        @await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
    </div>
    

@await Component.InvokeAsync マークアップは、ビュー コンポーネントを呼び出すための構文を示します。 最初の引数は、呼び出す必要があるコンポーネントの名前です。 後続のパラメーターは、そのコンポーネントに渡されます。 InvokeAsync では、任意の数の引数を取得できます。

アプリをテストします。 次の画像は、[ToDo] リストと優先順位の項目を示します。

[ToDo] リストと優先順位の項目

また、コントローラーから直接ビュー コンポーネントを呼び出すこともできます。

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

IndexVC アクションからの優先順位の項目

ビュー名を指定する

複雑なビュー コンポーネントでは、いくつかの条件下で、既定以外のビューを指定する必要がある場合があります。 次のコードは、メソッドから "PVC" ビューを指定する方法を InvokeAsync 示しています。 PriorityListViewComponent クラスで InvokeAsync メソッドを更新します。

public async Task<IViewComponentResult> InvokeAsync(
    int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

ファイルを Views/Shared/Components/PriorityList/Default.cshtml 名前付きの Views/Shared/Components/PriorityList/PVC.cshtmlビューにコピーします。 PVC ビューが使用されていることを示すために、見出しを追加します。

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

以下を更新します。Views/ToDo/Index.cshtml:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

アプリを実行して、PVC ビューを確認します。

優先順位のビュー コンポーネント

PVC ビューがレンダリングされない場合は、4 以上の優先順位でビュー コンポーネントを呼び出していることを確認します。

ビューのパスを調べる

  • 優先順位ビューが返されないように、優先順位パラメーターを 3 以下に変更します。

  • に一時的に名前を変更しますViews/ToDo/Components/PriorityList/Default.cshtml1Default.cshtml

  • アプリをテストすると、次のエラーが表示されます。

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    EnsureSuccessful
    
  • Views/ToDo/Components/PriorityList/1Default.cshtmlViews/Shared/Components/PriorityList/Default.cshtml にコピーします。

  • [Shared] の [ToDo] ビュー コンポーネントのビューにマークアップを追加して、そのビューが [Shared] フォルダーからのものであることを示します。

  • [Shared] コンポーネント ビューをテストします。

[Shared] コンポーネント ビューを含む [ToDo] 出力

ハードコーディングされた文字列の回避

コンパイル時間の安全性を確保する必要がある場合は、ハードコーディングされたビュー コンポーネント名をクラス名に置き換えることができます。 "ViewComponent" サフィックスのないビュー コンポーネントを作成します。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityList : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityList(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

using ステートメントをお使いの Razor ビュー ファイルに追加して、nameof 演算子を使用します。

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

    <h2>ToDo nameof</h2>
    <!-- Markup removed for brevity.  -->

    <div>

        @*
            Note: 
            To use the below line, you need to #define no_suffix in ViewComponents/PriorityList.cs or it won't compile.
            By doing so it will cause a problem to index as there will be multiple viewcomponents 
            with the same name after the compiler removes the suffix "ViewComponent"
        *@

        @*@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })*@
    </div>

CLR 型を受け取る Component.InvokeAsync メソッドのオーバーロードを使用できます。 この場合は、typeof 演算子を使用することを忘れないでください。

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

<h2>ToDo typeof</h2>
<!-- Markup removed for brevity.  -->

<div>
    @await Component.InvokeAsync(typeof(PriorityListViewComponent), new { maxPriority = 4, isDone = true })
</div>

同期作業を実行する

非同期作業を実行する必要がない場合は、フレームワークで同期 Invoke メソッドの呼び出しが処理されます。 次のメソッドでは同期 Invoke ビュー コンポーネントを作成します。

public class PriorityList : ViewComponent
{
    public IViewComponentResult Invoke(int maxPriority, bool isDone)
    {
        var items = new List<string> { $"maxPriority: {maxPriority}", $"isDone: {isDone}" };
        return View(items);
    }
}

ビュー コンポーネントのRazorファイルには、メソッド (Views/Home/Components/PriorityList/Default.cshtml) に渡される文字列がInvoke一覧表示されます。

@model List<string>

<h3>Priority Items</h3>
<ul>
    @foreach (var item in Model)
    {
        <li>@item</li>
    }
</ul>

ビュー コンポーネントは、次のいずれかの方法を Razor 使用してファイル (たとえば Views/Home/Index.cshtml) で呼び出されます。

IViewComponentHelper の方法を使用するには、Component.InvokeAsync を呼び出します。

@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })

タグ ヘルパーを使用するには、@addTagHelper ディレクティブを使用して、ビュー コンポーネントを含むアセンブリを登録します (ビュー コンポーネントは、MyWebApp と呼ばれるアセンブリ内にあります)。

@addTagHelper *, MyWebApp

Razor マークアップ ファイルでビュー コンポーネントのタグ ヘルパーを使用します。

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

PriorityList.Invoke のメソッド署名は同期的ですが、Razor ではマークアップ ファイルで Component.InvokeAsync を使用してメソッドを見つけて呼び出します。

ビュー コンポーネントのすべてのパラメーターが必要

ビュー コンポーネントの各パラメーターは、必須の属性です。 こちらの GitHub のイシューを参照してください。 いずれかのパラメーターを省略した場合は、次の手順を実行します。

  • InvokeAsync メソッドのシグネチャが一致しないため、メソッドが実行されません。
  • ViewComponent がマークアップをレンダリングしません。
  • エラーがスローされません。

その他の技術情報