檢視 ASP.NET Core 中的元件

作者:Rick Anderson

檢視元件

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

檢視元件:

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

如果可重複使用轉譯邏輯對於部分檢視而言太過複雜,則檢視元件是處理它的預定位置,例如:

  • 動態導覽功能表
  • 標籤雲端,可在其中查詢資料庫
  • [登入] 面板
  • 購物車
  • 最近發行的文章
  • 部落格上的資訊看板內容
  • 登入面板,將在每個頁面上轉譯並根據使用者登入狀態來示範登出或登入連結

檢視元件包含兩個部分:

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

與控制器類似,檢視元件可以是 POCO,但大部分開發人員都可以利用透過衍生自 ViewComponent 而取得的方法和屬性。

當不確定檢視元件是否符合應用程式的規格時,您可以考慮改用 Razor 元件。 Razor 元件同樣會結合標記與 C# 程式碼,來產生可重複使用的 UI 單元。 Razor 元件是為提升開發人員提供用戶端 UI 邏輯和組合時的生產力所設計。 如需詳細資訊,請參閱 ASP.NET Core Razor 元件。 如需如何將 Razor 元件整合至 MVC 或 Razor Pages 應用程式的詳細資訊,請參閱將 ASP.NET Core Razor 元件整合至 ASP.NET Core 應用程式

建立檢視元件

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

檢視元件類別

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

與控制器類似,檢視元件必須是公用、非巢狀和非抽象類別。 檢視元件名稱是移除 ViewComponent 尾碼的類別名稱。 它也可以使用 Name 屬性明確地指定。

檢視元件類別:

若要防止具有不區分大小寫 ViewComponent 尾碼的類別被視為檢視元件,請使用 [NonViewComponent] 屬性來裝飾類別:

using Microsoft.AspNetCore.Mvc;

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

檢視元件方法

檢視元件會在以下方式中的邏輯中定義:

  • 傳回 Task<IViewComponentResult>InvokeAsync 方法。
  • 傳回 IViewComponentResult 的同步方法 Invoke

參數直接來自檢視元件的引動過程,而不是來自模型繫結。 檢視元件絕不會直接處理要求。 通常,檢視元件會初始化模型,並呼叫 View 方法將其傳遞至檢視。 簡要來說,檢視元件方法:

  • 定義傳回 Task<IViewComponentResult>InvokeAsync 方法或傳回 IViewComponentResult 的同步 Invoke 方法。
  • 通常是透過叫用 ViewComponent.View 方法初始化模型並將其傳遞至檢視。
  • 參數來自呼叫端方法,而非 HTTP。 沒有模型繫結。
  • 無法直接當成 HTTP 端點連接。 其通常會在檢視中叫用。 檢視元件絕不會處理要求。
  • 已多載在簽章上,而非目前 HTTP 要求中的任何詳細資料。

檢視搜尋路徑

執行階段會搜尋下列路徑中的檢視:

  • /Views/{控制器名稱}/Components/{檢視元件名稱}/{檢視名稱}
  • /Views/Shared/Components/{檢視元件名稱}/{檢視名稱}
  • /Pages/Shared/Components/{檢視元件名稱}/{檢視名稱}
  • /Areas/{區域 Name}/Views/Shared/Components/{檢視元件名稱}/{檢視名稱}

搜尋路徑適用於使用控制器 + 檢視和 Razor Pages 的專案。

檢視元件的預設檢視名稱是 Default,這表示檢視檔案通常會命名為 Default.cshtml。 建立檢視元件結果時,或呼叫 View 方法時,可以指定不同的檢視名稱。

建議將檢視檔案命名為 Default.cshtml,並使用 Views/Shared/Components/{檢視元件名稱}/{檢視名稱} 路徑。 此範例中使用的 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>
    Maximum 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 項目清單。

List of 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 方法未傳遞檢視名稱,則依照慣例會使用 Default 作為檢視名稱。 若要覆寫特定控制器的預設樣式,請在控制器特定檢視資料夾中新增檢視 (例如 Views/ToDO/Components/PriorityList/Default.cshtml)。

    如果檢視元件是控制器特定的,則可以將其新增至控制器特定的資料夾。 例如,Views/ToDo/Components/PriorityList/Default.cshtml 是控制器特定的。

  • 將包含優先順序清單元件呼叫的 div 新增至 Views/ToDo/index.cshtml 檔案底端:

    </table>
    
    <div>
        Maximum 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 list and priority items

檢視元件可以直接從控制器進行呼叫:

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

priority items from IndexVC action

指定檢視元件名稱

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

Priority View Component

如果未轉譯 PVC 檢視,請驗證要呼叫的檢視元件優先順序是否為 4 或以上。

檢查檢視路徑

  • 將優先順序參數變更為 3 或更小,以不傳回優先順序檢視。

  • 暫時將 Views/ToDo/Components/PriorityList/Default.cshtml 重新命名為 1Default.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

  • 將某個標記新增至 Shared ToDO 檢視元件檢視,指出檢視來自 Shared 資料夾。

  • 測試 Shared 元件檢視。

ToDo output with Shared component view

避免硬式編碼的字串

針對編譯時間安全性,請將硬式編碼的檢視元件名稱取代為類別名稱。 將 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 尾碼的類別名稱。 它也可以使用 Name 屬性明確地指定。

檢視元件類別:

若要防止具有不區分大小寫 ViewComponent 尾碼的類別被視為檢視元件,請使用 [NonViewComponent] 屬性來裝飾類別:

using Microsoft.AspNetCore.Mvc;

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

檢視元件方法

檢視元件會在以下方式中的邏輯中定義:

  • 傳回 Task<IViewComponentResult>InvokeAsync 方法。
  • 傳回 IViewComponentResult 的同步方法 Invoke

參數直接來自檢視元件的引動過程,而不是來自模型繫結。 檢視元件絕不會直接處理要求。 通常,檢視元件會初始化模型,並呼叫 View 方法將其傳遞至檢視。 簡要來說,檢視元件方法:

  • 定義傳回 Task<IViewComponentResult>InvokeAsync 方法或傳回 IViewComponentResult 的同步 Invoke 方法。
  • 通常是透過叫用 ViewComponent.View 方法初始化模型並將其傳遞至檢視。
  • 參數來自呼叫端方法,而非 HTTP。 沒有模型繫結。
  • 無法直接當成 HTTP 端點連接。 其通常會在檢視中叫用。 檢視元件絕不會處理要求。
  • 已多載在簽章上,而非目前 HTTP 要求中的任何詳細資料。

檢視搜尋路徑

執行階段會搜尋下列路徑中的檢視:

  • /Views/{控制器名稱}/Components/{檢視元件名稱}/{檢視名稱}
  • /Views/Shared/Components/{檢視元件名稱}/{檢視名稱}
  • /Pages/Shared/Components/{檢視元件名稱}/{檢視名稱}
  • /Areas/{區域 Name}/Views/Shared/Components/{檢視元件名稱}/{檢視名稱}

搜尋路徑適用於使用控制器 + 檢視和 Razor Pages 的專案。

檢視元件的預設檢視名稱是 Default,這表示檢視檔案通常會命名為 Default.cshtml。 建立檢視元件結果時,或呼叫 View 方法時,可以指定不同的檢視名稱。

建議將檢視檔案命名為 Default.cshtml,並使用 Views/Shared/Components/{檢視元件名稱}/{檢視名稱} 路徑。 此範例中使用的 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>
    Maximum 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 項目清單。

List of 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 方法未傳遞檢視名稱,則依照慣例會使用 Default 作為檢視名稱。 若要覆寫特定控制器的預設樣式,請在控制器特定檢視資料夾中新增檢視 (例如 Views/ToDO/Components/PriorityList/Default.cshtml)。

    如果檢視元件是控制器特定的,則可以將其新增至控制器特定的資料夾。 例如,Views/ToDo/Components/PriorityList/Default.cshtml 是控制器特定的。

  • 將包含優先順序清單元件呼叫的 div 新增至 Views/ToDo/index.cshtml 檔案底端:

    </table>
    
    <div>
        Maximum 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 list and priority items

檢視元件可以直接從控制器進行呼叫:

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

priority items from IndexVC action

指定檢視元件名稱

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

Priority View Component

如果未轉譯 PVC 檢視,請驗證要呼叫的檢視元件優先順序是否為 4 或以上。

檢查檢視路徑

  • 將優先順序參數變更為 3 或更小,以不傳回優先順序檢視。

  • 暫時將 Views/ToDo/Components/PriorityList/Default.cshtml 重新命名為 1Default.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

  • 將某個標記新增至 Shared ToDO 檢視元件檢視,指出檢視來自 Shared 資料夾。

  • 測試 Shared 元件檢視。

ToDo output with Shared component view

避免硬式編碼的字串

針對編譯時間安全性,請將硬式編碼的檢視元件名稱取代為類別名稱。 將 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/{檢視元件名稱}/{檢視名稱}
  • /Areas/{區域 Name}/Views/Shared/Components/{檢視元件名稱}/{檢視名稱}

搜尋路徑適用於使用控制器 + 檢視和 Razor Pages 的專案。

檢視元件的預設檢視名稱是 Default,這表示您的檢視檔案通常會命名為 Default.cshtml。 建立檢視元件結果時,或呼叫 View 方法時,可以指定不同的檢視名稱。

建議您將檢視檔案命名為 Default.cshtml,並使用 Views/Shared/Components/{檢視元件名稱}/{檢視名稱} 路徑。 此範例中使用的 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 方法是使用兩個參數所呼叫:

@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 項目清單。

List of 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 list and priority items

您也可以直接從控制器呼叫檢視元件:

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

priority items from IndexVC action

指定檢視名稱

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

Priority View Component

如果未轉譯 PVC 檢視,請驗證您要呼叫的檢視元件優先順序為 4 或以上。

檢查檢視路徑

  • 將優先順序參數變更為 3 或更小,以不傳回優先順序檢視。

  • 暫時將 Views/ToDo/Components/PriorityList/Default.cshtml 重新命名為 1Default.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

  • 將某個標記新增至 Shared ToDO 檢視元件檢視,指出檢視來自 Shared 資料夾。

  • 測試 Shared 元件檢視。

ToDo output with Shared component view

避免硬式編碼的字串

如果您想要編譯時間安全,則可以將寫在程式碼中的檢視元件名稱取代為類別名稱。 建立不含 "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 不會轉譯任何標記。
  • 系統不會擲回任何錯誤。

其他資源