本篇文章將介紹ASP.Net Core中Partial View
及View Component
的使用方式。
同步發表於個人點部落 - [鐵人賽Day13] ASP.Net Core MVC 進化之路 - View(3) / Partial View及View Component
Partial View中文翻成部分顯示或局部顯示,
可將功能錯雜的畫面切割成較小的元件,
適當使用可避免產生過多重複的HTML。
在過去HTML Helper中使用Partial View,
可依同步方式及串流輸出方式分為@Html.Partial
、@Html.PartialAsync
、@Html.RenderPartial
、@Html.RenderPartialAsync
四種方式。
而ASP.Net Core MVC保有原始Partial View的特性,
可透過Html Helper及Tag Helper呼叫Partial View。
但Tag Helper的Partial View
本身就直接是Async,
使用上也較容易閱讀,
所以筆者比較推薦使用Tag Helper的方式(ASP.Net Core的Tag Helper很強大!),
命名的話可參考官方建議檔名以_
開頭Partial
結尾,
如_PokemonPartial.cshtml
。
當Response夾帶資料回到View時,
View在呼叫Partial View時會將data指定給Partial View,
此時Partial View會自動繼承View所擁有的model
與viewdata
,
筆者個人習慣以「父子關係
」來形容View
與Partial View
的關係。
請注意,由於Partial View與View是發生在同一條Request-Response上,
所以Partial View本身並不會直接與Response互動,
換句話說,Partial View僅負責接收資料後呈現畫面的工作而已。
有關Partial View的lifecycle
可參考下圖,
下面使用Partial View方式設計Pokemon的列表。IronmentController.cs
public IActionResult Index()
{
var pokemons = new List<Pokemon>()
{
new Pokemon()
{
Id = 1,
Name = "水箭龜",
Property = "水系"
},
new Pokemon()
{
Id = 2,
Name = "噴火龍",
Property = "火系"
},
new Pokemon()
{
Id = 3,
Name = "妙蛙花",
Property = "草系"
}
};
return View(pokemons);
}
Index.cshtml
@model IEnumerable<IronmenMvcWeb.Models.Pokemon>
@foreach (var item in Model)
{
<partial name="_PokemonDetailPartial" model="item" />
}
_PokemonDetailPartial.cshtml
@model IronmenMvcWeb.Models.Pokemon
<div>
<hr />
<dl class="dl-horizontal">
<dt>編號</dt>
<dd>@Model.Id</dd>
<dt>屬性</dt>
<dd>
@Model.Property
</dd>
<dt>名稱</dt>
<dd>@Model.Name</dd>
</dl>
</div>
輸出結果
PartialTagHelper中還可設定許多參數,
最常用的是name
跟model
,
而for
的設計用途是給Razor Page用的,
因此不能與model
同時使用。
本系列是學習ASP.Net Core MVC,
使用model
較單純也較直覺。
如果需依不同Partial View傳遞自訂的ViewData
,
則可使用view-data
屬性。
最後要介紹的是View Component,
通常講到Partial View就會搭配Child Action(ASP.Net MVC5)一起介紹。
前面提到Partial View是用來拆解View中重複性較高的區塊,
但如果前端邏輯較複雜時Partial View就不適用了,
在過去我們會使用Child Action將畫面上可獨立的功能抽取出來,
不過也因為它擁有獨立的生命週期導致效能較為不佳。
而ASP.Net Core MVC移除了Child Action後推出了View Component,
官方還很貼心地提供適用情境:
接著介紹View Component的特性:
Controller
回傳的ActionResult
。Default.cshtml
(名稱可另外指定)。@Component.InvokeAsync()
有關View Component生命週期可參考下圖。
從圖中我們可以發現,
Result Filter只有在回傳View Result時,
才會叫用View Engine將View行轉譯(將.cshtml
組裝成.html
),
而轉譯過程中若發現@Component.InvokeAsync("component_name", new { eid = "3"})
,
指定名稱後可透過匿名類別注入參數(key-value),
接著叫用對應的View Component,
並將渲染後的結果(HTML)回傳給View Engine。
以下簡單實作一個天氣看板,
我們先建立待會需要用到的類別。Weather.cs
public class Weather
{
//為方便實作故統一使用string
public string Location { get; set; }
public string Temperature { get; set; }
public string Humidity { get; set; }
public string RainProbability { get; set; }
public string Status { get; set; }
}
WeatherService.cs
public class WeatherService
{
public Weather GetWeather(string location, DateTime date)
{
var data = this.GetFakeWeatherData();
return data.SingleOrDefault(x => x.Location == location
&& x.Date == date);
}
private List<Weather> GetFakeWeatherData()
{
var fakeData = new List<Weather>()
{
new Weather()
{
Date = new DateTime(2018, 10, 1),
Location = "台北市",
Humidity = "38%",
Temperature = "24-28度C",
RainProbability = "40%",
Status = "晴天"
},
new Weather()
{
Date = new DateTime(2018, 10, 1),
Location = "桃園市",
Temperature = "24-30度C",
Humidity = "35%",
RainProbability = "20%",
Status = "晴天"
},
new Weather()
{
Date = new DateTime(2018, 10, 1),
Location = "宜蘭縣",
Temperature = "24-28度C",
Humidity = "80%",
RainProbability = "65%",
Status = "雨天"
}
};
return fakeData;
}
}
接著建立ViewComponent
類別,
官方提供了三種實作方式:
以下使用第一種繼承的方式示範。WeatherBoardViewComponent.cs
public class WeatherBoardViewComponent : ViewComponent
{
private WeatherService weather;
public WeatherBoardViewComponent(WeatherService _weather)
{
weather = _weather;
}
public async Task<IViewComponentResult> InvokeAsync(string location, DateTime date)
{
var weather = weather.GetWeather(location, date);
return View(weather);
}
}
接著我們需建立對應的View,
而View擺放的位置是有限制的(因為View Engine在組裝時只會尋找以下可能途徑):
WeatherBoardViewComponent
的<view_component_name>為WeatherBoard
。<view_name>可使用預設的Default.cshtml
,
如果要自訂名稱記得在InvokeAsync()
中return View()
時設定。
public async Task<IViewComponentResult> InvokeAsync(string location, DateTime date)
{
var weather = weatherService.GetWeather(location, date);
return View("MyWeatherBoard", weather);
}
我們使用Default.cshtml
實作。
@model IronmenMvcWeb.Models.Weather
<div class="alert alert-success">
<dl class="dl-horizontal">
<dt>日期</dt>
<dd>@Model.Date.ToShortDateString()</dd>
<dt>地區</dt>
<dd>
@Model.Location
</dd>
<dt>溫度</dt>
<dd>@Model.Temperature</dd>
<dt>濕度</dt>
<dd>@Model.Humidity</dd>
<dt>天氣</dt>
<dd>@Model.Status</dd>
</dl>
</div>
最後在Index.cshtml
中呼叫ViewComponent。
@model IEnumerable<IronmenMvcWeb.Models.Pokemon>
<h2>View Component</h2>
@await Component.InvokeAsync("WeatherBoard", new { location = "台北市", date = new DateTime(2018, 10, 1) })
執行結果。
Partial View及View Component的篇幅就介紹到這邊,
若內容有問題歡迎討論指教。
https://docs.microsoft.com/zh-tw/aspnet/core/mvc/views/partial?view=aspnetcore-2.1
https://docs.microsoft.com/zh-tw/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper?view=aspnetcore-2.1