概述
用户界面由业务逻辑确定,业务逻辑又由数据确定。完成 Model(数据)的设计后,便可以开始设计控制器(业务逻辑)和视图(用户界面)。
Layout 视图
除非是那种只有一两个页面的小 Web 程序,否则我们总是从要共享使用的 Layout 视图开始设计。
从 Layout 视图开始的一大好处是,它可以概述 Web 程序的大纲,并作为控制器的目录。例如,如果在 Layout 导航栏中添加了 Products 和 Types 链接,就意味着将来一定会添加 ProductController 和 TypeController。
创建 Layout 视图
_Layout.cshtml 必须创建在 Views\Shared\ 文件夹下:
<html>
<!--use table as the navigation bar-->
<table>
<tr>
<td>|@Html.ActionLink("Home", "Index", "Home")</td>
<td>|@Html.ActionLink("Products", "Index", "Product")</td>
<td>|@Html.ActionLink("Types", "Index", "ProductType")</td>
</tr>
</table>
<hr/>
@RenderBody()
<hr/>
Welcome to My Website! (@DateTime.Now)
</html>
视图使用此 Layout 作为模板时,其内容将呈现在 @RenderBody() 处。
应用 Layout 视图
为了将 _Layout.cshtml 设置为默认视图,需在 Views 文件夹下创建 _ViewStart.cshtml 并添加如下代码:
@{
Layout = "_Layout";
}
如果有的视图不想使用该默认 Layout,可以在视图顶部添加代码:
@{
Layout = null;
}
Home 的控制器和视图
首先添加控制器是用于渲染 Web 程序主页的 HomeController。
添加 HomeController
希望你还记得默认的路由模板:{controller=Home}/{action=Index}/{id?}
。
在 Controllers 文件夹下创建 HomeController:
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Layout 视图中 @Html.ActionLink("Home", "Index", "Home")
生成的超链接就链接到该控制器的 Index 方法。
添加 Index 视图
在 Views\Hone 文件夹下创建 Index.cshtml:
<h1>Welcome to Use Product Manager</h1>
<h2>Introduction</h2>
<p>With this simple web application, you can manage the products and product types.<br/>
To manage products, please click the Products link. To manage product types, please click the Types link.</p>
添加 Index 视图后,运行程序访问主页,效果如下:
注:此时由于另外两个视图尚未实现,所以点击它们的链接会报 404 Not Found。
ProductType 的控制器和视图
主页创建后,接着就是创建功能性的控制器。因为 Product 是依赖于 ProductType 的,所以要先创建 ProductType 的控制器和视图。
ProductType 的控制器和 Index 操作
创建功能性控制器时,我们通常从创建一个空 Controller 并添加 Index 操作开始。众所周知功能性控制器的主要工作就是 CRUD,与 Retrieve 对应的操作就是 Index。
ProductTypeController.cs:
using Microsoft.AspNetCore.Mvc;
using ProductManager.Models;
namespace ProductManager.Controllers
{
public class ProductTypeController : Controller
{
public IActionResult Index()
{
var types = DataSource.GetProductTypes();
return View(types);
}
// other actions will be here
}
}
添加 Index 视图
在 Views\ProductType 文件夹下创建 Index.cshtml:
@model List<ProductManager.Models.ProductType>
<h1>All Product Types</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Product Count</th>
<th>Delete</th>
<th>Edit</th>
<th>Products</th>
</tr>
@foreach (var pt in Model) {
<tr>
<td>@pt.ID</td>
<td>@pt.Name</td>
<td>@pt.Products.Count</td>
<td>
@if (pt.CanBeRemoved) {
@Html.ActionLink("Delete", "Delete", new { id = pt.ID })
} else {
<span>--</span>
}
</td>
<td>@Html.ActionLink("Edit", "Edit", new { id = pt.ID })</td>
<td>@Html.ActionLink("Products", "IndexByTypeID", "Product", new { id = pt.ID })</td>
</tr>
}
</table>
@Html.ActionLink("Add a New Product Type", "Create")
顶部的 @model List <ProductManager.Models.ProductType>
表示该视图的 View Model(视图模型)是 List
添加 Create 操作和视图
通常在添加 Index 后,紧跟着添加的就是 Create。这样也方便创建产品类型后在 Index 页面进行查看。
CRUD 里 Create 和 Update 通常都对应一对方法,一个用来页面跳转,一个实际执行操作。
Create Action:
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(ProductType productType)
{
if (ModelState.IsValid) {DataSource.AddProductType(productType);}
return RedirectToAction("Index");
}
Create.cshtml:
@model ProductManager.Models.ProductType
<h1>Add a New Product Type</h1>
@using (Html.BeginForm()) {
@Html.LabelFor(pt => pt.Name)
@Html.TextBoxFor(pt=>pt.Name)
<input type="submit" value="Add" />
}
Model 属性绑定的类型是 ProductType
使用
Html.BeginForm()
生成了创建产品类型的表单
添加 Delete 操作
Delete 删除 ProductType 后就直接跳转回 Index 页面,所以省了视图。
Delete Action:
public IActionResult Delete(int id)
{
DataSource.RemoveProductTypeByID(id);
return RedirectToAction("Index");
}
添加 Edit 操作和视图
Edit 和 Create 功能非常相似,唯一区别就是编辑时要跟踪记录 ProductType 的 ID。一般通过 <input type="hidden/>
实现。
Edit Action:
[HttpGet]
public IActionResult Edit(int id)
{
var pt = DataSource.GetProductTypeByID(id);
return View(pt);
}
[HttpPost]
public IActionResult Edit(ProductType productType)
{
if (ModelState.IsValid)
{
DataSource.UpdateProductTypeByID(productType.ID, productType);
}
return RedirectToAction("Index");
}
Edit.cshtml:
@model ProductManager.Models.ProductType
<h1>Edit Product Type</h1>
@using (Html.BeginForm()) {
@Html.HiddenFor(pt=>pt.ID)
@Html.LabelFor(pt=>pt.Name)
@Html.TextBoxFor(pt=>pt.Name)
<input type="submit" value="Update" />
}
不难看出 Edit 视图和 Create 相差无几,而实际项目中也经常使用一个视图。
Product 的控制器和视图
上一节中,我们学习了如何创建控制器及其视图 —— 从 Index 的操作和视图开始,然后是 Create 和 Delete,最后是 Edit。现在,让我们继续按照这个顺序创建 Product 的控制器和视图。
Product 的控制器和 Index
Controllers\ProductController:
using Microsoft.AspNetCore.Mvc;
using ProductManager.Models;
namespace ProductManager.Controllers {
public class ProductController : Controller {
public IActionResult Index() {
ViewData["Title"] = "All Products";
var products = DataSource.GetProducts();
return View(products);
}
public IActionResult IndexByTypeID(int id) {
var pt = DataSource.GetProductTypeByID(id);
ViewData["Title"] = $"Products of {pt.Name}";
var products = DataSource.GetProductsByTypeID(id);
return View("Index", products); // reuse Index view
}
// other actions
}
}
与 ProductTypeController 不同,ProductController 有两个 Index 操作。第一个返回所有产品的列表,第二个按产品类别返回产品列表。回顾 ProductType 的 Index 页面里面的 @Html.ActionLink("Products", "IndexByTypeID", "Product", new { id = pt.ID })
,很明显它生成的超链接对应此处的第二个操作。
因为 Index 和 IndexByTypeID 都将显式产品列表,所以它俩可以共享视图文件。
Views\Product\Index.cshtml:
@model IList<ProductManager.Models.Product>
<h1>@ViewData["Title"]</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
<th>Price</th>
<th>Delete</th>
<th>Edit</th>
<th>Detail</th>
</tr>
@foreach (var p in Model) {
<tr>
<td>@p.ID</td>
<td>@p.Name</td>
<td>@p.Type.Name</td>
<td>@p.Price</td>
<td>@Html.ActionLink("Delete", "Delete", new { id = p.ID })</td>
<td>@Html.ActionLink("Edit", "Edit", new { id = p.ID })</td>
<td>@Html.ActionLink("Detail", "ShowDetail", new { id = p.ID })</td>
</tr>
}
</table>
@Html.ActionLink("Add a New Product", "Create")
Master-Detail 的操作和视图
Web 程序可以通过主从页(master-detail page)来渲染有主从关系的数据。主从页的想法是在 master 页呈现主题的简要信息,并在简要信息中提供 detail 页的入口。
上面的 Index 视图就是 master 页,页面中的 @Html.ActionLink("Detail", "ShowDetail", new { id = p.ID })
就是 detail 页的入口。
下面是 ShowDetail 操作的代码:
public IActionResult ShowDetail(int id) {
var product = DataSource.GetProductByID(id);
if (product != null)
return View("Detail", product);
else
return RedirectToAction("Index");
}
Views\Product\Detail.cshtml:
@model ProductManager.Models.Product
<h1>Product Detail</h1>
<table border="1">
<tr>
<td>@Html.DisplayNameFor(p => p.ID)</td>
<td>@Html.DisplayFor(p => p.ID)</td>
</tr>
<tr>
<td>@Html.DisplayNameFor(p => p.Name)</td>
<td>@Html.DisplayFor(p => p.Name)</td>
</tr>
<tr>
<td>@Html.DisplayNameFor(p => p.Type)</td>
<td>@Html.ActionLink(Model.Type.Name, "IndexByTypeID", new { id = Model.TypeID })</td>
</tr>
<tr>
<td>@Html.DisplayNameFor(p => p.Price)</td>
<td>@Html.DisplayFor(p => p.Price)</td>
</tr>
</table>
现在来测试 Index 和 Detail 视图。运行程序。导航到 Product 页面然后单击任意一个 Detail 链接,你应该会看到以下页面:
添加 Create 和 Delete
和 ProductTypeController 代码类似:
[HttpGet]
public IActionResult Create() {
return View();
}
[HttpPost]
public IActionResult Create(Product product) {
if (ModelState.IsValid)
DataSource.AddProduct(product);
return RedirectToAction("Index");
}
public IActionResult Delete(int id) {
DataSource.RemoveProductByID(id);
return RedirectToAction("Index");
}
Views\Product\Create.cshtml:
@using ProductManager.Models
@model Product
<h1>Add a New Product</h1>
@using (Html.BeginForm()) {
<table>
<tr>
<td>@Html.LabelFor(p => p.Name)</td>
<td>@Html.TextBoxFor(p => p.Name)</td>
</tr>
<tr>
<td>@Html.LabelFor(p => p.Price)</td>
<td>@Html.TextBoxFor(p => p.Price)</td>
</tr>
<tr>
<td>@Html.LabelFor(p => p.Type)</td>
<td>
@Html.DropDownListFor(p => p.TypeID,
new SelectList(DataSource.GetProductTypes(), "ID", "Name"))
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add" /></td>
</tr>
</table>
}
大部分代码都很容易理解。需要注意的地方是:
@Html.DropDownListFor(p => p.TypeID,
new SelectList(DataSource.GetProductTypes(), "ID", "Name"))
它将被渲染成:
<select id="TypeID" name="TypeID">
<option value="1">Book</option>
<option value="2">Fruit</option>
</select>
现在来测试 Create 和 Delete。运行程序。创建并删除产品,你应该会看到以下页面:
添加 Edit 操作和视图
基于 Create 操作和视图,我们可以很快的编写 Edit 的代码:
[HttpGet]
public IActionResult Edit(int id) {
var product = DataSource.GetProductByID(id);
return View(product);
}
[HttpPost]
public IActionResult Edit(Product product) {
if (ModelState.IsValid)
DataSource.UpdateProductByID(product.ID, product);
return RedirectToAction("Index");
}
Views\Product\Edit.cshtml:
@using ProductManager.Models
@model Product
<h1>Edit Product</h1>
@using (Html.BeginForm()) {
@Html.HiddenFor(p => p.ID)
<table>
<tr>
<td>@Html.LabelFor(p => p.Name)</td>
<td>@Html.TextBoxFor(p => p.Name)</td>
</tr>
<tr>
<td>@Html.LabelFor(p => p.Price)</td>
<td>@Html.TextBoxFor(p => p.Price)</td>
</tr>
<tr>
<td>@Html.LabelFor(p => p.Type)</td>
<td>
@Html.DropDownListFor(p => p.TypeID,
new SelectList(DataSource.GetProductTypes(), "ID", "Name"))
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Update" /></td>
</tr>
</table>
}
最后,让我们来测试一下 Edit 功能:
恭喜你!你实现了一个包含整个课程内容的 ASP.NET Core Web 程序。