檢視 ASP.NET Core 中的元件

作者:Rick Anderson

檢視元件

檢視元件與部分檢視類似,但功能更強大。 檢視元件不會使用模型系結,它們取決於呼叫檢視元件時所傳遞的資料。 本文是使用控制器和檢視撰寫,但檢視元件可搭配Pages 使用 Razor

檢視元件:

  • 轉譯區塊,而不是整個回應。
  • 包含控制器與檢視之間的相同關注點分離和可測試性優點。
  • 可以有參數和商務邏輯。
  • 它通常是從配置頁面叫用。

檢視元件適用于任何可重複使用的轉譯邏輯,對於部分檢視而言太複雜,例如:

  • 動態導覽功能表
  • 標記雲端,它會在其中查詢資料庫
  • 登入面板
  • 購物車
  • 最近發行的文章
  • 部落格上的提要欄內容
  • 會在每個頁面上呈現的登入面板,並根據使用者的登入狀態顯示登出或登入的連結

檢視元件包含兩個部分:

  • 類別,通常衍生自 ViewComponent
  • 其傳回的結果通常是檢視。

就像控制器一樣,檢視元件可以是 POCO,但大部分開發人員都可以利用衍生自 ViewComponent 的方法和屬性。

考慮檢視元件是否符合應用程式的規格時,請考慮改用 Razor 元件。 Razor 元件也會結合標記與 C# 程式碼,以產生可重複使用的 UI 單位。 Razor 元件是針對提供用戶端 UI 邏輯和組合時的開發人員生產力所設計。 如需詳細資訊,請參閱ASP.NET Core Razor 元件。 如需如何將元件並 Razor 入 MVC 或 Razor Pages 應用程式的資訊,請參閱預先呈現並整合 ASP.NET Core Razor 元件

建立檢視元件

本節包含建立檢視元件的高階需求。 在本文稍後,我們會詳細檢查每個步驟,並建立檢視元件。

檢視元件類別

您可以透過下列任一項來建立檢視元件類別:

與控制器類似,檢視元件必須是公用、非巢狀和非抽象類別。 檢視元件名稱是已移除尾碼的 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 頁面的專案。

檢視元件的預設檢視名稱為 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 使用兩個參數呼叫 方法:

</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>

標籤協助程式依照 Pascal 命名法大小寫慣例的類別和方法參數會轉譯成其 Kebab 字體。 用來叫用檢視元件的標籤協助程式會使用 <vc></vc> 項目。 檢視元件指定如下:

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

若要使用檢視元件作為標籤協助程式,請使用 @addTagHelper 指示詞註冊包含檢視元件的組件。 如果檢視元件位於名為 MyWebApp 的元件中,請將下列指示詞新增至 _ViewImports.cshtml 檔案:

@addTagHelper *, MyWebApp

檢視元件可以註冊為標籤協助程式,以參考檢視元件的任何檔案。 如需如何註冊標籤協助程式的詳細資訊,請參閱管理標籤協助程式範圍

本教學課程中使用的 InvokeAsync 方法:

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

在上述標記中,檢 PriorityList 視元件會 priority-list 變成 。 檢視元件的參數會以 Kebab 字體傳遞為屬性。

直接從控制器叫用檢視元件

檢視元件通常會從檢視中叫用,但可以直接從控制器方法叫用它們。 雖然檢視元件未定義控制器之類的端點,但可以實作傳回 內容的 ViewComponentResult 控制器動作。

在下列範例中,檢視元件會直接從控制器呼叫:

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

建立基本檢視元件

下載、建置和測試起始程式碼。 它是具有 ToDo 控制器的基本專案,可顯示 ToDo 專案清單。

ToDos 清單

更新控制器以傳入優先順序和完成狀態

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] 屬性可以變更用來參考檢視元件的名稱。 例如,類別可能已使用下列 [ViewComponent] 屬性命名 XYZ

    [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 方法未傳遞檢視的名稱, 預設 會依慣例用於檢視名稱。 若要覆寫特定控制器的預設樣式,請在控制器特定檢視資料夾中新增檢視 (例如 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 動作中的優先順序項目

指定檢視元件名稱

在某些情況下,可能需要複雜的檢視元件,才能指定非預設檢視。 下列程式碼示範如何從 InvokeAsync 方法指定 「PV」 檢視。 更新 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 檢視。

設定檢視元件優先順序

如果未轉譯PV 檢視,請確認呼叫優先順序為 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.cshtml 複製到 Views/Shared/Components/PriorityList/Default.cshtml

  • 將一些標記新增至 共用 ToDo 檢視元件檢視,以指出檢視來自 共用資料夾

  • 測試 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) ,而傳回的結果通常是檢視) (。 就像控制器一樣,檢視元件可以是 POCO,但大部分開發人員都可以利用衍生自 ViewComponent 的方法和屬性。

考慮檢視元件是否符合應用程式的規格時,請考慮改用 Razor 元件。 Razor 元件也會結合標記與 C# 程式碼,以產生可重複使用的 UI 單位。 Razor 元件是針對提供用戶端 UI 邏輯和組合時的開發人員生產力所設計。 如需詳細資訊,請參閱ASP.NET Core Razor 元件。 如需如何將元件並 Razor 入 MVC 或 Razor Pages 應用程式的資訊,請參閱預先呈現並整合 ASP.NET Core 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 頁面的專案。

檢視元件的預設檢視名稱是 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}」 內的檢視,請將新專案新增至集合:

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

在上述程式碼中,預留位置 「 {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 方法是使用兩個參數所呼叫:

@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>

標籤協助程式依照 Pascal 命名法大小寫慣例的類別和方法參數會轉譯成其 Kebab 字體。 用來叫用檢視元件的標籤協助程式會使用 <vc></vc> 項目。 檢視元件指定如下:

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

若要使用檢視元件作為標籤協助程式,請使用 @addTagHelper 指示詞註冊包含檢視元件的組件。 如果您的檢視元件位於名為 MyWebApp 的元件中,請將下列指示詞新增至 _ViewImports.cshtml 檔案:

@addTagHelper *, MyWebApp

您可以將檢視元件註冊為任何參考檢視元件的檔案標籤協助程式。 如需如何註冊標籤協助程式的詳細資訊,請參閱管理標籤協助程式範圍

本教學課程中使用的 InvokeAsync 方法:

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

在標籤 (tag) 協助程式標籤 (markup) 中:

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

在上述範例中,PriorityList 檢視元件會變成 priority-list。 檢視元件的參數會以 Kebab 字體傳遞為屬性。

直接從控制器叫用檢視元件

檢視元件通常是從檢視中進行叫用,但您可以直接從控制器方法叫用它們。 雖然檢視元件不會定義控制器這類端點,但您可以輕鬆地實作控制器動作,以傳回 ViewComponentResult 的內容。

在此範例中,是直接從控制器呼叫檢視元件:

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

逐步解說:建立簡單檢視元件

下載、建置和測試起始程式碼。 它是具有 ToDo 控制器的簡單專案,而此控制器顯示 ToDO 項目清單。

ToDos 清單

新增 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] 屬性可以變更用來參考檢視元件的名稱。 例如,類別可能已使用 ViewComponent 屬性命名 XYZ

    [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 動作中的優先順序項目

指定檢視名稱

在某些情況下,可能需要複雜的檢視元件,才能指定非預設檢視。 下列程式碼示範如何從 InvokeAsync 方法指定 「PV」 檢視。 更新 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.cshtml 複製到 Views/Shared/Components/PriorityList/Default.cshtml

  • 將一些標記新增至 共用 ToDo 檢視元件檢視,以指出檢視來自 共用資料夾

  • 測試 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 檔案會列出傳遞至 Invoke 方法的字串 (Views/Home/Components/PriorityList/Default.cshtml) :

@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 不會轉譯任何標記。
  • 系統不會擲回任何錯誤。

其他資源