ASP.NET Core应用程序5:控制器和视图(二)

1 准备工作

在Startup中启用会话配置。

services.AddDistributedMemoryCache();
services.AddSession(options =>
{
    options.Cookie.IsEssential = true;
});
app.UseSession();

2 使用ViewBag

操作方法使用视图模型提供数据,但有时还需额外信息。可以使用ViewBag提供额外数据。

public async Task<IActionResult> Index(long id = 1)
{
    ViewBag.AveragePrice = await _context.Products.AverageAsync(a=>a.Price);
    var model = await _context.Products.FindAsync(id);
    return View(model);
}

ViewBag属性是从Controller基类继承来的,它返回一个dynameic对象。可以在视图中访问操作方法赋给ViewBag的值。

<tr>
    <th>Price</th>
    <td>
        @Model.Price.ToString("c")
        平均值的@((Model.Price / ViewBag.AveragePrice * 100).ToString("F2"))%
    </td>
</tr>

3 使用TempData

添加CubedController。

    public class CubedController : Controller
    {
        public IActionResult Index()
        {
            return View("Cubed");
        }
        public IActionResult Cube(double num)
        {
            TempData["value"] = num.ToString();
            TempData["result"] = Math.Pow(num, 3).ToString();
            return RedirectToAction(nameof(Index));
        }
    }

定义了一个Index方法返回Cubed视图。Cube方法执行计算,将结果值保存到TempData中,该属性用来存储键值对。当把值存储为临时数据后,Cube方法重定向到Index方法。
在Views/Shard文件夹下添加Cubed.cshtml。

<!DOCTYPE html>
<html>
  <head>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
  </head>
  <body>
    <h6 class="bg-secondary text-white text-center m-2 p-2">Cubed</h6>
    <form method="get" action="/cubed/cube" class="m-2">
      <div class="form-group">
        <label>Value</label>
        <input name="num" class="form-control" value="@(TempData["value"])" />
      </div>
      <button class="btn btn-primary" type="submit">Submit</button>
    </form>
    @if (TempData["result"] != null)
    {
    <div class="bg-info text-white m-2 p-2">
      @TempData["value"]的立方是 @TempData["result"]
    </div>
    }
  </body>
</html>

在本例中使用临时数据设置了一个input元素内容,并显示一个结果摘要。读取一个临时数据并不会立刻删除,同一个视图中就可以重复读取值,只有当处理请求完成后,才会删除标记的值。

4 使用布局

示例视图包含重复元素,为了避免这种重复,Razor支持布局,可通过将公共内容放到一个文件中供视图使用。
通常把布局放到Views/Shared文件夹下。添加_Layout.cshtml。

<!DOCTYPE html>
<html>
  <head>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
  </head>
  <body>
    <div>
      <h6 class="bg-primary text-white text-center m-2 p-2">共享视图</h6>
      @RenderBody()
    </div>
  </body>
</html>

布局包含了供多个视图使用的公共内容。每个视图所特有的内容是通过@RenderBody()方法插入响应,RazorPage类继承了该方法。
在Views/Home/Index.cshtml中使用布局。

@model Product
@{
    Layout = "_Layout";
}
<div class="m-2">
    <table class="table table-sm table-striped table-bordered">
        <tbody>
            <tr><th>Name</th><td>@Model.Name</td></tr>
            <tr>
                <th>Price</th>
                <td>
                    @Model.Price.ToString("c")
                    (平均值的@((Model.Price / ViewBag.AveragePrice * 100).ToString("F2"))%)
                </td>
            </tr>
            <tr><th>Category ID</th><td>@Model.CategoryId</td></tr>
            <tr><th>SupplierId</th><td>@Model.SupplierId</td></tr>

        </tbody>
    </table>
</div>

4.1 使用ViewBag配置布局

视图可为布局提供数据值,从而允许定制视图提供的公共内容。ViewBag属性是在选择布局的代码块中定义的。

@{
    Layout = "_Layout";
    ViewBag.Title = "产品表";
}

在_Layout.cshtml中用ViewBag。

<!DOCTYPE html>
<html>
  <head>
    <title>@ViewBag.Title</title>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
  </head>
  <body>
    <div>
      <h6 class="bg-primary text-white text-center m-2 p-2">
        @(ViewBag.Title ?? "共享视图")
      </h6>
      @RenderBody()
    </div>
  </body>
</html>

4.2 使用ViewStart文件

不必在每个视图中设置Layout属性,而是可以在项目中添加一个ViewStart文件,用来提供默认的Layout值。在Views文件下添加_ViewStart.cshtml。

@{
    Layout = "_Layout";
}

从Views/Shared文件夹的Common.cshtml文件中删除布局包含内容。

<h6 class="bg-secondary text-white text-center m-2 p-2">共享视图</h6>

4.3 覆盖默认布局

在两种情况下,即使在项目中定义了_ViewStart文件,也可能需要在视图中定义Layout属性。
第一种情况,视图需要的布局和_ViewStart指定的布局不同。在Views/Shared文件夹下添加一个_ImportantLayout.cshtml布局文件。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div>
        <h6 class="bg-primary text-white text-center m-2 p-2">
            重要布局
        </h6>
        @RenderBody()
    </div>
</body>
</html>

在Views/Home/Index.cshtm中使用特定布局。Layout将被视图中的值覆盖,从而允许应用不同的布局。

@model Product
@{
    Layout = "_ImportantLayout";
    //Layout = Model.Price > 100 ? "_ImportantLayout":"_Layout";
    ViewBag.Title = ViewBag.Title ?? "产品表";
}

第二种情况,视图包含一个完整的html文件,并不需要一个布局,可将Layout值设置为null。

@{
    Layout = null;
}

4.4 使用布局节

视图引擎支持节的概念,从而允许在布局内提供区域内容。
在Home/Index.cshtml中定义节。

}
@section Header{
    产品信息
}

<tr><th>Name</th><td>@Model.Name</td></tr>
<tr>
    <th>Price</th>
    <td>
        @Model.Price.ToString("c")
    </td>
</tr>
<tr><th>Category ID</th><td>@Model.CategoryId</td></tr>

@section Footer{
    (平均值的@((Model.Price / ViewBag.AveragePrice * 100).ToString("F2"))%)
}

使用@RenderSection表达式在布局内应用节,应用布局时@RenderSection把指定节内容插入响应,没有包含再节内的由方法插入响应。如果定义了节但布局中没有使用将会报错。
修改_Layout.cshtml文件中使用节。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div class="bg-info  text-white m-2 p-2">
        这是布局的一部分
    </div>
    <h6 class="bg-primary text-white text-center m-2 p-2">
        @RenderSection("Header")
    </h6>
    <div class="bg-info  text-white m-2 p-2">
        这是布局的一部分
    </div>
    <div class="m2">
        <table class="table table-sm table-striped table-bordered" data-id="@Model.ProductId">
            <tbody>
                @RenderBody();
            </tbody>
        </table>
    </div>
    <div class="bg-info  text-white m-2 p-2">
        这是布局的一部分
    </div>
    <h6 class="bg-primary text-white text-center m-2 p-2">
        @RenderSection("Footer")
    </h6>
    <div class="bg-info text-white m-2 p-2">
        这是布局的一部分
    </div>
</body>
</html>

节允许视图向布局提供内容片段,而不指定如何使用他们。以下把body和节合并到一个html表格中。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div class="m2">
        <table class="table table-sm table-striped table-bordered">
            <thead>
                <tr>
                    <th class="bg-primary text-white text-center" colspan="2">
                        @RenderSection("Header")
                    </th>
                </tr>
            </thead>
            <tbody>
                @RenderBody();
            </tbody>
            <tfoot>
                <tr>
                    <th class="bg-primary text-white text-center" colspan="2">
                        @RenderSection("Footer")
                    </th>
                </tr>
            </tfoot>
        </table>
    </div>
</body>
</html>

(1)使用可选布局节
@RenderSection第二个实参,只有当视图定义了该节时才渲染该节。

@RenderSection("Header",false)

(2)测试布局节
IsSectionDefined用于判断是否定义了指定节。以下如果没有定义则渲染后备内容。

    @if (IsSectionDefined("Summary"))// 判断视图是否定义了节
    {
        @RenderSection("Summary", false)
    }
    else
    {
        <div class="bg-info text-center text-white m-2 p-2">
            这是默认摘要
        </div>
    }

5 使用分部视图

5.1 启动分部视图

通过标签助手可以应用分部视图。
在_ViewImports.cshtml中启用标签助手。

@using MyWebApp.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

5.2 创建分部视图

分部视图也是.cshtml文件,只是使用方式与标准视图区分开。
在Home文件夹下创建_RowPartial.cshtml。

@model Product

<tr>
    <td>@Model.Name</td>
    <td>@Model.Price</td>
</tr>

5.3 应用分部视图

通过在另一个视图或布局中添加partial元素,可以应用分部视图。
在Home/List.cshtml中使用。

            <thead>
                <tr><th>Name</th><th>Price</th><th>平均占比</th></tr>
            </thead>
            <tbody>
                @foreach (Product p in Model)
                {
                    <partial name="_RowPartial" model="p" />
                }
            </tbody>

注:早期版本中使用@Html.Partial("_RowPartial"),现在仍然支持。

(0)使用表达式选择分部视图模型
for特性使用一个应用到视图模型的表达式,设置分部视图模型。
在Views/Home文件夹下添加_CellPartial.cshtml。

@model string
<td class="bg-info text-white" >@Model</td>

在_RowPartial.cshtml中使用。

@model Product
<tr>
    @*<td>@Model.Name</td>*@
    <partial name="_CellPartial" for="Name" />
    <td>@Model.Price</td>
</tr>

6 理解内容编码

Razor视图为内容提供了两种有用功能。html内容编码确保了表达式响应不会修改发送给浏览器的响应结构。json编码功能将对像编码为json并将其插入响应中。

6.1 理解html编码

在HomeController中添加一个方法。

        public IActionResult Html()
        {
            return View((object)"this is a <h3><i>string</i></h3>");
        }

在Views/Home文件夹中添加Html.cshtml。

@model string
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
    <div class="bg-secondary text-white text-center m-2 p-2">@Model</div>
    <div class="bg-secondary text-white text-center m-2 p-2">@Html.Raw(Model)</div>
</body>
</html>

调用@Html.Raw(Model),它返回对象实现了IHtmlHeper接口,模型字符串将会被浏览器解释为html一部分。

6.2 理解json编码

在Index.cshtml中使用json编码

@section Summary {
    <div class="bg-info text-white m-2 p-2">
        @Json.Serialize(Model)
    </div>
}

Json属性返回IJsonHelper接口的一个实现,@Json.Serialize(Model)生成对象的json表示。

posted @ 2024-04-24 10:47  一纸年华  阅读(19)  评论(0编辑  收藏  举报