《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法

时间:2023-03-09 02:52:47
《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法

5.1 表单使用

5.1.1 action 和 method 特性

<form action="/Home/Index">
    <input name="q" type="text"/>
    <input value="提交" type="submit" />

</form>  

如果没有 method 默认是: get 方法。

5.1.2 GET 方法还是 POST 方法

       get 请求的所有参数都在URL中,因此可以为GET请求建立书签。除此之外,还可以保留所有的表单输入值。
      因为GET不会改变服务器上的状态,所以客户端可以向服务端重复发送GET请求而不会产生负面影响。
      通常在Web程序中,GET 请求用于读操作,POST 请求用于写操作(通常包括更新,创建和删除)。

<form action="/Home/Index" method="get">
    <input name="q" type="text"/>
    <input value="提交" type="submit" />

</form>  

      BeginForm Html 辅助方法
@using (Html.BeginForm("Search","Home",FormMethod.Get))
{
    <input name="q" type="text"/>
    <input value="提交" type="submit" />
}  
      BeginForm Html 的辅助方法利用路由引擎找到 HomeController 控制器的 Search 操作。它在后台使用 GetVirtualPath 方法,该方法在 Routetable 的 Routes 属性中——在 global.asax 中,应用程序注册所有路由的位置。

    public class RouteTable
    {
        // 摘要: 
        //     获取从 System.Web.Routing.RouteBase 类派生的对象的集合。
        // 返回结果: 
        //     包含集合中的所有路由的对象。
        public static RouteCollection Routes { get; }

}  


      不采用HTML辅助方法,将编写所有代码:

@{
    var context = this.ViewContext.RequestContext;
    var values = new RouteValueDictionary {
    {"controller","home"},{"action","index" }
    };
    var path = RouteTable.Routes.GetVirtualPath(context, values);
}
<form action="@path.VirtualPath" method="get">
    <input name="q" type="text" />
    <input value="提交" type="submit" />

</form>  

5.2 HTML辅助方法

5.2.1 自动编码

      本章许多辅助方法都可以输出模型值。所有这些输出模型值的方法都会在渲染前,对值进行HTML编码。

@Html.TextArea("text","hello <br/> world");

TextArea 辅助方法的第二个参数是要渲染的值。

<textarea cols="20" id="text" name="text" rows="2">
    hello &lt;br/&gt; world
</textarea>

5.2.2 辅助方法的使用(匿名对象 htmlAttributes,属性@class = "editForm",连字符data_validatable = true)

      保护代码的同时,辅助方法给出了适当的控制。为了展示辅助方法的作用,下列给出 BeginForm 另外一个重载版本:

@using (Html.BeginForm("Search", "Home", FormMethod.Get,new {target ="_blank"}))
{
    <input name="q" type="text" />
    <input value="提交" type="submit" />

}

        // 参数: 
        //   actionName:
        //     操作方法的名称。
        //
        //   controllerName:
        //     控制器的名称。
        //
        //   method:
        //     用于处理窗体的 HTTP 方法(GET 或 POST)。
        //
        //   htmlAttributes:

//     一个对象,其中包含要为该元素设置的 HTML 特性。

public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, FormMethod method, object htmlAttributes);

      向 BeginForm 方法的 htmlAttribute 参数传递一个匿名类型的对象,在MVC框架重载版本中,几乎每一个HTML辅助方法都包含 htmlAttribute 参数,有时发现某些重载版本中 htmlAttribte 参数类型是 IDctionary<string,object> 。辅助方法利用字典条目创建辅助方法生成元素的特性。

<form action="/Home/Search" method="get" target="_blank">

      设置元素的 class 特性就要求匿名类型对象上必须有一个名为 class 的属性,或者值的字典中有一个 class 的键。在字典中有一个“class”键值不是问题,问题在对象中有一个 class 的属性。
        因为 class 是 C# 的关键字,不能用作属性名称或标识符,所以要在c lass 前面加一个 @符号作为前缀:

@using (Html.BeginForm("Search", "Home", FormMethod.Get,new {target ="_blank",@class = "editForm"}))
{...}

结果:

<form action="/Home/Search" class="editForm" method="get" target="_blank">

      另一个问题是将属性设置为带连字符的名称(像 data-val)。
        所有 HTML 辅助方法在渲染 HTML 时会将属性名中的下划线转换为连字符

@using (Html.BeginForm("Search", "Home", FormMethod.Get, new { target = "_blank", @class = "editForm", data_validatable = true }))
{...}

结果:

<form action="/Home/Search" class="editForm" data-validatable="True" method="get" target="_blank">

5.2.3 HTML 辅助方法工作原理

      每一个 Razor 视图都继承了它们的基类的 Html 属性。Html 的属性类型是 System.Web.Mvc.HtmlHelper<T> 。
当方法名称左边有一个向下的蓝色箭头时,说明这个方法是一个扩展方法。
《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法

5.2.4 设置专辑编辑表单


@using (Html.BeginForm())
{
    @Html.ValidationSummary(excludePropertyErrors:true)
    <fieldset>
        <legend>Edit Album</legend>
        <p><input type="submit" value="Save"/></p>
    </fieldset>

}  


这段代码有两个辅助方法:

1.Html.BeginForm

      这里不带参数的 BeginForm 向当前发送一个 POST 请求,如果响应了 

<form action="/Home/Test" method="post">  

2.Html.ValidationSummary(生成错误验证摘要)

      ValidationSummary 用来显示 ModelState 字典中所有验证错误的无序列表。告知 ValidationSummary 方法只显示 ModelState 中与模型本身有关错误,而不显示那些与具体模型属性相关的错误。

ModelState.AddModelError("TermsAccepted", "You must accept the terms");  
 

5.2.5 添加输入元素

1.Html.TextBox 和 Html.TextArea

      Html.TextBox 会渲染一个 type 特性为 text 的 input 标签。

@Html.TextBox("Title",Model.Title)  
生成的HTML:<input id="Title" name="Title" type="text" value="Caravan">

@Html.TextArea("text","hello <br/> world")  
生成的HTML:<textarea cols="20" id="text" name="text" rows="2">hello &lt;br/&gt; world</textarea>

@Html.TextArea("text", "hello <br/> world", 10, 80, null)  
生成的HTML:<textarea cols="80" id="text" name="text" rows="10">hello &lt;br/&gt; world</textarea>

2.Html.Lable

       返回一个<lable>元素,并使用 String 类型参数决定渲染的文本和 for 特性值。(提示:"for" 属性可把 label 绑定到另外一个元素。请把 "for" 属性的值设置为相关元素的 id 属性的值。)

@Html.Label("GenreId")  
生成的HTML:<label for="GenreId">GenreId</label>

3.Html.DropDownList 和 Html.ListBox

       DropDownList(单选) 和 ListBox(多选) 都返回一个 <select/> 元素。
       下拉列表需要一个包含所有可选项的 SelectListItem 对象集合,其中每一个 SelectListItem 对象包含 Text , Value 和 Selected 三个属性。也可以使用框架中的 SelectList 或 MultiSelectList 辅助方法来构建。这些类可以查看任意类型的 IEnumerable 对象并将其转换为 SelectListItem 对象的序列。

/// <summary>
/// 使用列表的指定项、数据值字段、数据文本字段和选定的值来初始化 System.Web.Mvc.SelectList 类的新实例
/// </summary>
/// <param name="items">各个项</param>
/// <param name="dataValueField">数据值字段</param>
/// <param name="dataTextField">数据文本字段</param>
/// <param name="selectedValue">选定的值</param>

public SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue);

 
例如,StoreManager 控制器中的 Edit 操作:

后台:

        public ActionResult CreateMyTest(int id = 0)
        {
            Album album = db.Albums.Find(id);
            if (album == null)
            {
                return HttpNotFound();
            }
            ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
            ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
            return View(album);

}  

前台:
@Html.DropDownList("GenreId", "请选择")  

生成的HTML:

<select id="GenreId" name="GenreId">
<option value="">请选择</option>
<option selected="selected" value="1">Al Di Meola</option>
</select>

4.Html.ValidationMessage

       当 ModelState 字典中的某一特定字段出现错误时,可以使用 ValidationMessage 辅助方法显示相应的错误提示消息。下面故意在模型状态中为 Title 属性添加一个错误:

        public ActionResult CreateMyTest(int id = 0)
        {
            Album album = db.Albums.Find(id);
            ModelState.AddModelError("Title", "what a terrible name!");
            return View(album);

}


在视图中使用:@Html.ValidationMessage("Title")


生成的 Html: 
<span class="field-validation-error" data-valmsg-for="Title" 
data-valmsg-replace="true">
what a terrible name!
</span>

也可以调用 ValidationMessage 重写方法来重写错误提示:@Html.ValidationMessage("Title","Something is wrong with your title")


5.2.6 辅助方法、模型和视图数据

      辅助方法如 Html.TextBox 和 Html.DropDownList(以及其他所有表单辅助方法) 检查 ViewData 对象以获取要显示的当前值(在 ViewBag 对象中所有值也可以通过 ViewData 得到)

        public ActionResult Test2(int id)
        {
            ViewBag.Price = 10;
            return View();

}


视图中,使用 ViewBag 中的值为 TextBox 辅助方法命名,显示价格的文本框:@Html.TextBox("Price")

<input data-val="true"  id="Price" name="Price" type="text" value="10">

当辅助方法查看 ViewData 内容时,能看到其中对象属性。下面修改控制器:

        public ActionResult Test2(int id)
        {
            ViewBag.Album = new Album { Price = 11 };
            ViewBag.Price = 10;
            return View();

}


视图修改:

@Html.TextBox("Album.Price")

输出HTML:
<input data-val="true" id="Album_Price" name="Album.Price" type="text" value="11">

《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法
《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法
         如果 ViewData 没有匹配 “Album.Price” 的值,那辅助方法将尝试查找与第一个点之前那部分名称(Album)匹配的值。换言之,就是找一个 Album 类型的对象。然后估测名称中剩余部分(Price),并找到相应的值。
         注意得到 input 元素的 id 特性值使用下划线代替了点(但name特性依然使用点)。因为 id 特性中包含点是非法的。因此,运行时用静态属性 HtmlHelper.IdAttributeDotReplacement 的值代替了点。如果没有有效的 id 特性,就无法执行带有 javascript 库的客户端脚本。

5.2.7 强类型辅助方法

      如果不适应从视图数据中提取值,可以使用 MVC 提供的强类型辅助分类方法。只需要提供一个 lambda 表达式来指定要渲染的模型属性。

@model MVC4.Models.Album  

添加了模型指令,可以使用下面的代码:

@Html.LabelFor(m=>m.GenreId)

@Html.DropDownListFor(m => m.GenreId, ViewBag.Genres as SelectList, "请选择")  

 
       静观代码生成与前面同样的 HTML标记,但用 lambda 表达式代替字符串有其他好处,包括智能感应、编译时检查和轻松的代码重构。

5.2.8 辅助方法和模型元数据

      辅助方法不仅查看 VIewData 内部数据,也利用可得到的模型元数据。

@Html.LabelFor(m=>m.GenreId) 

文本生成:<label for="GenreId">流派</label> 
       文本“流派”从哪里来的?当辅助方法询问运行时是否有 GenreId 的可用模型元数据时,运行时从装饰 Album 模型的 DIsplayName 特性中获取的信息。

        [DisplayName("流派")]

public virtual int GenreId { get; set; }  


5.2.9 模版辅助方法

       MVC 中的模版辅助方法利用元数据和模版构建 HTML 。
        元数据包括关羽模型值(它的名称和类型)的信息和(通过数据注解或自定义提供器添加的)模型元数据。模版辅助方法有 Html.Display 和 Html.Editor ,以及分别与它们对应的强类型方法 Html.DisplayFor 和 Html.EditorFor ,还有它们对应到完整模型 Html.DisplayForModel 和 Html.EditorModel 。
      例如 Html.TextBoxFor 辅助方法为某个专辑的 Title 属性生成HTML标记:

@Html.TextBoxFor(m => m.Title)

<input id="Title" name="Title" type="text" value="Caravan">

也可以使用 EditorFor 方法取代:

@Html.EditorFor(m => m.Title)

下面为 Title 属性添加一个 DataType 注解:

        [Required(ErrorMessage ="An Album Title is required")]
        [StringLength(160)]
        [DataType(DataType.MultilineText)]

public virtual string Title { get; set; }


生成的HTML:
<textarea class="text-box multi-line" data-val="true" data-val-length="字段 Title 必须是最大长度为 160 的字符串。" data-val-length-max="160" data-val-required="An Album Title is required" id="Title" name="Title">
Caravan
</textarea>

5.2.10 辅助方法和 ModelState

用来显示表单的值的所有辅助方法需要与 ModelState 交互。
      用来渲染表单字段的辅助方法自动在 ModelState 字典中查找它们的当前值。辅助方法使用名称表达式作为键,在 ModelState 字典进行查找。如果查找的值已在 ModelState 中,辅助方法就用 ModelState 的值替换视图数据中的当前值。
      模型绑定失败后, ModelState 查找表中允许保存“坏”值。例如,用户向 DateTime 属性编辑器中输入值 "abc",模型绑定就会失败,并且“abc” 也会保存在模型状态相关属性中。为了在用户修改验证错误而重新渲染视图时,“abc”值依然出现在 DateTime 编辑器中,可让用户看到刚才尝试的错误文本并允许他们修改。
      当 ModelState 包含某个属性的错误时,与错误相关的表单辅助方法除了显示渲染指定 CSS 类之外,还会渲染 input-validation-error CSS 类。

5.3 其他输入辅助方法


5.3.1 Html.Hidden

@Html.Hidden("wizard", "值")
生成的Html:
 <input type="hidden" id="wizard" name="wizard" value="值" />

5.3.2 Html.passwrod

@Html.Password("wizard")
生成的Html:
<input type="password" id="wizard" name="wizard" />

5.2.3 Html.RadioButtion

单选按钮一般组合起一起用。
@Html.RadioButton("color", "red")
@Html.RadioButton("color", "blue",true)

@Html.RadioButton("color", "green")

生成的HTML:
<input id="color" name="color" type="radio" value="red">
<input checked="checked" id="color" name="color" type="radio" value="blue">

<input id="color" name="color" type="radio" value="green">


5.2.4 Html.CheckBox

唯一渲染两个输入元素的方法
@Html.CheckBox("IsDiscuounted")
生成的Html:
<input id="IsDiscuounted" name="IsDiscuounted" type="checkbox" value="true">

<input name="IsDiscuounted" type="hidden" value="false">



5.4 渲染辅助方法

5.4.1 Html.ActionLink 和 Html.RouteLink


1.Html.ActionLink能够渲染一个超链接。
@Html.ActionLink("text", "index")
生成HTML:
<a href="/home/index">text</a>

2.当需要指向一个不同控制器的时候
@Html.ActionLink("index", "MenuList","Menu")
生成的HTML:
<a href="/Menu/MenuList">index</a>

3.传递参数,内置的 ActionLink没有提供处理版本。
      但是,其中一个版本允许向它传递一个 RouteValueDictionary 类型对象;另外一个版本允许给 routeValues 参数传递一个对象(通常是匿名类型的)。

例如,构建一个指向ID号为10720的专辑编辑页面的链接
@Html.ActionLink("Edit link text", "Edit", "StoreManager", new { id = 10720 }, null)
       上面重载最后一个参数是 htmlAttributes。传递了一个 null (实际上没有设置 HTML 元素上的任何特性值)。尽管未设置任何特性,但是为了调用 ActionLink 这个重载方法,必须传递一个参数。

生成的HTML:
<a href="/StoreManager/Edit/10720">Edit link text</a>

4.尽管 RouteLink 方法和 ActionLink 方法遵循相同模式,但 RouteLink 只接收路由名称,而不能接收控制器名称和操作名称。
      例如,演示 ActionLink 的第一个例子也可以用下面代码,
@Html.RouteLink("Link Text2",new{action="AnotherAction"})
生成HTML:
<a href="/Home/AnotherAction">Link Text2</a>

5.4.2 URL 辅助方法

1 Url.Action

    Action 方法与 ActionLink 相似,但它不返回锚标签。

<span>
@Url.Action("browse", "Store", new { genre = "Jazz" }, null)

</span>

生成的HTML:
<span>
/Store/browse?genre=Jazz
</span>

2 Url.Content

Content 方法特别有用,因为它可以把应用程序的相对路径换成绝对路径。
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.10.2.js")"></script>

3 Url.RouteUrl

RouteUrl 辅助方法与 Action 方法遵循同样的模式,但与 RouteLink 一样,只接收路由名称,而不接收控制器和操作名称。

5.4.3 Html.Partial 和 Html.RenderPartial

1.Html.Partial

Partial有4个重载版本
public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName);
public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model);
public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData);

public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData);


这里没必要为视图指定路径和文件扩展名,因为运行时定位部分视图与定位正常视图使用的逻辑相同。
@Html.Partial("MenuForm")

2.Html.RenderPartial

RenderPartial 与 Partial 非常相似,但 RenderPartial 不返回字符串,而是直接写入响应输出流。
出于这个原因,必须把 RenderPartial 放入代码块中,而不能放入代码表达式中。

(1)错误写法
@Html.RenderPartial("MenuList")
《ASP.NET MVC高级编程(4版)》读书笔记(5)表单和HTML辅助方法
(2)正确写法
@{Html.RenderPartial("MenuList");}

5.4.4 Html.Action 和 Html.RederAction

1.首先看一下它们的对等关系
@Html.Partial 对应 @{Html.RenderPartial();}
@Html.Action 对应 @{Html.RenderAction();}

2.Action 加载方法的视图,执行 Controller → Model → View 的顺序,然后把产生的页面带回到原来的View中再回传。而Partial直接加载视图文件内容

3.@Html.Partial 可以直接提供用户控件名作为参数,而 Html.Action 需要有对应的 Action ,在 Action 内部返回 PartailResult (即retun PartialView()) 。