8 Partial 视图
部分视图是普通的视图文件(.cshtml),可以嵌入到另外的视图文件里,这意味这相同的视图文件能被使用在多个地方并且减少代码重复,如果在我们应用程序中有重复的视图,我们可以将这个视图作为部分视图,在别的视图中加载这个文件,这种方式可以阻止代码重复
在Views->Shared目录下添加TestPratialView.cshtml视图.
@model List<string><div class="border border-warning">This is partial View:<ul>@foreach (string str in Model){<li>@str</li>}</ul></div>
在TestLayout.cshtml视图中添加如下代码:
@await Html.PartialAsync("TestPartialView", new List<string> {"Classic ASP", "ASP.NET Web Forms", "ASP.NET MVC", "ASP.NET Core MVC"})

9 视图组件
视图组件有些像部分视图但是又有一些不同,视图组件相比部分视图更强大,我们可以在里面创建服务器的逻辑,这是和部分视图完全不同的
1 在站点中创建身份验证面板,提供用户在不访问单独登录页面的情况下登录
2 根据用户的角色动态创建一个导航菜单
3 购物车面板,显示当前购物车中的产品
4 依赖性注射特征
视图组件是C#类继承于ViewComponent基类,视图组件必须定义一个Invoke()方法或者InvokeAsync()异步方法,在此方法中,视图组件必须执行为其创建的任务
视图组件可以在应用程序的任何地方创建,但是根据约定,我们一般创建在应用程序根目录下的Components文件夹
9.1 例子
using Microsoft.AspNetCore.Mvc;namespace AspNetCore.Views.Components{public class Cart : ViewComponent{public string Invoke(){return "This is from View Component";}}}
这个视图组件仅仅返回一个字符串消息,现在,从视图使用@await Component.InvokeAsync("NameofViewComponent")调用这个视图组件,这将调用视图组件中的Invoke方法
在_Layout.cshmtl中添加@await Component.InvokeAsync("Cart") 在页面顶部,代码如下:
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width" /><title>@ViewData["Title"]</title><link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /></head><body><header></header><div class="p-3 mb-2 border border-info">@await Component.InvokeAsync("Cart")</div></body></html>

9.2 视图组件的返回类型
在前面我们学习了关于视图组件返回字符串,视图组件也能返回IViewComponentResult接口通过调用Invoke方法,使用IViewComponentResult接口视图组件能够返回string,html,和partial view
| 类名 | 描述 | 
| ContentViewComponentResult | 返回编码的HTML.例如Content("<h2>some text</h2>") | 
| HtmlContentViewComponentResult | 返回没有编码的HTML.例如HtmlContentViewComponentResult("<h2>some text</h2>") | 
| ViewViewComponentResult | 返回部分视图. Eg View("NameofView", model) | 
让我们演示这个,修改Cart视图组件使用Content()方法返回IViewComponentResult类型
public IViewComponentResult Invoke(){return Content("This is from <h2>View Component</h2>");}
运行应用程序进行测试

如果检查页面源代码你会发现HTML被编码
This is from <h2>View Component</h2>public IViewComponentResult Invoke(){return new HtmlContentViewComponentResult(new HtmlString("This is from <h2>View Component</h2>"));}
运行应用,你将会看到如下信息展示在浏览器中

你可以使用视图组件返回部分视图,ViewComponent基类提供了View()方法返回部分视图
有4个版本的View方法:
View();//选择默认部分视图View(model);//选择默认部分视图并提供数据模型给它View("viewname");//通过名字选择部分视图View("viewname",model);//通过名字选择视图并且提供数据模型给它
/Views/{controller}/Components/{view component}/{partial view name}/Views/Shared/Components/{view component}/{partial view name}
让我们创建一个复杂视图组件返回部分视图,创建一个模型类Product.cs在Models文件夹:
namespace AspNetCore.Views.Models{public class Product{public string Name { get; set; }public int Price { get; set; }}}
public IViewComponentResult Invoke(){Product[] products = new Product[] {new Product() { Name = "Women Shoes", Price = 99 },new Product() { Name = "Mens Shirts", Price = 59 },new Product() { Name = "Children Belts", Price = 19 },new Product() { Name = "Girls Socks", Price = 9 }};return View(products);}

注意控制器处理HTTP请求是HomeController并且我们没有在View()中指定视图名称,因此ASP.Net Core会从下面位置搜索视图
/Views/Home/Components/Cart/Default.cshtml/Views/Shared/Components/Cart/Default.cshtml
@model Product[]<table class="table" style="width:50%"><thead class="thead-dark"><tr><td><u>Product Name</u></td><td><u>Price</u></td></tr></thead><tbody>@{foreach (Product p in Model){<tr class="table-secondary"><td>@p.Name</td><td>@p.Price</td></tr>}}</tbody></table>

9.7 在视图组件中使用DI
namespace AspNetCore.Views.Models{public class Coupon{public string GetCoupon(){//get coupons from external database or an external web api callreturn "Discount10";}}}
builder.Services.AddTransient<Coupon>();using AspNetCore.Views.Models;using AspNetCore.Views.Service;using Microsoft.AspNetCore.Mvc;namespace AspNetCore.Views.Components{public class Cart : ViewComponent{private Coupon _coupon;public Cart(Coupon coupon){_coupon = coupon;}#region 返回复杂视图public IViewComponentResult Invoke(){Product[] products = new Product[] {new Product() { Name = "Women Shoes", Price = 99 },new Product() { Name = "Mens Shirts", Price = 59 },new Product() { Name = "Children Belts", Price = 19 },new Product() { Name = "Girls Socks", Price = 9 }};ViewBag.Coupon = _coupon.GetCoupon();return View(products);}#endregion}}
现在我们在Default.cshtml视图中展示折扣信息,读取ViewBag变量并展示
@model Product[]<table class="table" style="width:50%"><thead class="thead-dark"><tr><td><u>Product Name</u></td><td><u>Price</u></td></tr></thead><tbody>@{foreach (Product p in Model){<tr class="table-secondary"><td>@p.Name</td><td>@p.Price</td></tr>}}</tbody></table><p class="p-3 text-white bg-dark">Apply coupon - @ViewBag.Coupon</p>
运行应用程序并展示

9.8 父组件中的值传递给子组件
我们可以从父组件向子组件传递至,使用@await Component.InvokeAsync()的第二个参数提供一个匿名对象,在_Layout.cshtml文件修改InvokeAsync()传递一个false属性:
@await Component.InvokeAsync("Cart",new {showCart=false})using AspNetCore.Views.Models;using AspNetCore.Views.Service;using Microsoft.AspNetCore.Mvc;namespace AspNetCore.Views.Components{public class Cart : ViewComponent{private Coupon _coupon;public Cart(Coupon coupon){_coupon = coupon;}#region 返回复杂视图public IViewComponentResult Invoke(bool showCart){Product[] products;if (showCart){products = new Product[] {new Product() { Name = "Women Shoes", Price = 99 },new Product() { Name = "Mens Shirts", Price = 59 },new Product() { Name = "Children Belts", Price = 19 },new Product() { Name = "Girls Socks", Price = 9 }};ViewBag.Coupon = _coupon.GetCoupon();}else{products = new Product[] { };}return View(products);}#endregion}}
10 匿名视图组件
异步视图组件使用指定异步任务,InvokeAsync方法返回一个task对象,ASP.NET Core将等待任务完成并且在view中呈现结果
using Microsoft.AspNetCore.Mvc;namespace AspNetCore.Views.Components{public class PageSize: ViewComponent{public async Task<IViewComponentResult> InvokeAsync(){HttpClient client = new HttpClient();HttpResponseMessage response = await client.GetAsync("http://www.msn.com");return View(response.Content.Headers.ContentLength);}}}
异步视图组件将获取MSN页数使用HTTP GET 请求并且将传递页数到default视图
在View/Home/Components/PageSize目录下创建一个Default.cshtml目录,在文件夹中添加下面代码:
@model long<div class="p-3 mb-2 text-danger">Page size: @Model</div>
@await Component.InvokeAsync("PageSize")
using System.Text.Json;namespace AspNetCore.Views.Service{public class Joke{public async Task<string> GetJoke(){string apiResponse = "";using (var httpClient = new HttpClient()){using (var response = await httpClient.GetAsync("https://api.vvhan.com/api/joke")){apiResponse = await response.Content.ReadAsStringAsync();}}return apiResponse;}}}
该类有一个名为 GetJoke 的方法,该方法调用 Web API 并取名为joke的笑话,最后返回,接下来,在 Program.cs 类中添加Joke为transient 服务
builder.Services.AddTransient<Joke>();public IActionResult Joke(){return View();}
在Views->Home目录下添加Joke.cshtml代码如下
@using AspNetCore.Views.Service@inject Joke joke;<h2 class="text-primary">Today's Joke</h2><p>@await joke.GetJoke()</p>
运行程序,进入URL- https://localhost:7019/Home/Joke,会发现我们每次随机展示一个笑话

总结