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 call
return "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,会发现我们每次随机展示一个笑话
总结