若要在 MVC 應用程式中使用 HTML 控制項,除了直接建立 HTML 控制項之外, 也可以使用 System.Web.Mvc.Html 命名空間底下的擴充控制項,來協助建立 HTML 控制項。 該命名空間包括支援表單、輸入控制項、連結、部分檢視和驗證等的類別。
FormExtensions
FormExtensions 類別是來用產生 <Form> 標籤用的,它包含三個擴充方法,分別是:
- BeginForm :
- BeginRouteForm :
- EndForm :
@{Html.BeginForm("Edit", "Employees");} @Html.HiddenFor(model => model.EmployeeID) <div> FirstName: @Html.TextBox("FirstName") @Html.ValidationMessageFor(model => model.FirstName) </div> <div> LastName:@Html.TextBox("LastName") @Html.ValidationMessageFor(model => model.LastName) </div> <div> Title:@Html.TextBox("Title") </div> <div> BirthDate:@Html.TextBox("BirthDate") </div> <div> Age:@Html.TextBox("Age") </div> <input type="submit" value="Save" /> @{Html.EndForm();}
產生內容
<form action="/Employees/Edit" method="post"> <input id="EmployeeID" name="EmployeeID" type="hidden" value="10" /> <div> FirstName: <input id="FirstName" name="FirstName" type="text" value="vito2" /> <span data-valmsg-for="FirstName" data-valmsg-replace="true"></span> </div> <div> LastName: <input id="LastName" name="LastName" type="text" value="vito2" /> <span data-valmsg-for="LastName" data-valmsg-replace="true"></span> </div> ... <input type="submit" value="Save" />
以下是 BeginForm 的另一種用法,可讀性較高:
@using (Html.BeginForm("Edit", "Employees")) { @Html.HiddenFor(model => model.EmployeeID) <div> @Html.LabelFor(model => model.FirstName) @Html.EditorFor(model => model.FirstName) @Html.ValidationMessageFor(model => model.FirstName) </div> <div> @Html.LabelFor(model => model.LastName) @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName) </div> <div> @Html.LabelFor(model => model.Title) @Html.EditorFor(model => model.Title) </div> <div> @Html.LabelFor(model => model.BirthDate) @Html.EditorFor(model => model.BirthDate) </div> <div> @Html.LabelFor(model => model.Age) @Html.EditorFor(model => model.Age) </div> <input type="submit" value="Save"/> }
BeginForm 有多個多載方法,其中一個 BeginForm(actionName, controllerName, method) 可以用來設定表單提交時,要使用哪一種 action method 處理。
LabelExtensions 、 DisplayExtensions 、 DisplayTextExtensions
通常為了在 View 中顯示特定資料,我們會在 Controller 中,將資料先儲存在 ViewData 中,再由 View 中取出。如下面範例:
public ActionResult About() { ViewData["Message"] = "歡迎使用 ASP.NET MVC!"; }
@ViewData["Message"].ToString()
MVC 提供我們幾個更簡便的方式以顯示這些資料。 你可以透過 DisplayExtensions 類別提供的下列方法,將物件資料,以 HTML 格式呈現:
Display 方法通常用來顯示 ViewData 字典中的資料; DisplayFor 和 DisplayForModel 方法通常用來顯示 Model 屬性所公開之物件的値。
[DisplayName("員工")] public class Person { [DisplayName("編號")] public int ID { get; set; } [Required()] [DisplayName("大名")] public string FirstName { get; set; } [Required(ErrorMessage = "請輸入LastName")] [DisplayName("姓氏")] public string LastName { get; set; } [Required()] [MaxLength(10)] [DisplayName("電話")] public string Tel { get; set; } [DisplayName("已婚")] public bool Merried { get; set; } }
LabelExtensions
LabelExtensions 會使用 <label> 標籤來轉譯指定的 Property 的 name 。它包含底下幾個方法:
- Label :使用字串指定 Property ,回傳該 Peoperty 的顯示名稱。
- LabelFor :使用 Expression 指定 Property ,回傳該 Peoperty 的顯示名稱。
- LabelForModel :這個方法,隱含地使用模型。回傳該 Model 的 Property 名稱,若不指定 Property 則回傳 Model 的顯示名稱。
@Html.Label("FirstName", new { @class = "test classA" }) <%--回傳 property 設定的 DisplayName--%> @Html.LabelFor(m => m.FirstName, new { @class = "test classB" }) @Html.LabelForModel("FirstName") <%--回傳 property name--%> @Html.LabelForModel("FirstName", new { @class = "test classA" }) @Html.LabelForModel(new { @class = "test classB" }) <%--若不指定 property , 會回傳 model name--%>
<label class="test classA" for="FirstName">大名</label> <label class="test classB" for="FirstName">大名</label> <label for="">FirstName</label> <label class="test classA" for="">FirstName</label> <label class="test classA" for="">學生</label>
DisplayTextExtensions
DisplayTextExtensions 會直接將指定的 Property 的 value 轉譯出去。它包含底下幾個方法:
- DisplayText (string name) :使用字串指定 Property
- DisplayTextFor (Expression expression) :使用 Expression 指定 Property
<div> @Html.DisplayText("FirstName") </div> <div> @Html.DisplayTextFor(m => m.FirstName) </div>
<div> vito </div> <div> vito </div>
DisplayExtensions
DisplayExtensions 會直接將指定的 Property 的 value 轉譯出去,而且支援使用 template 來設定輸出的樣式。它包含底下幾個方法:
- Display :使用指定的範本,傳回由字串指定的 Property 的 Value 。
- DisplayFor :使用指定的範本,傳回由 Expression 指定的 Property 的 Value 。
- DisplayForModel :使用指定的範本,傳回模型中每一個屬性的 Name 和 Value 。
@Html.Display("Tel", "myTelTemplate") @Html.DisplayFor(m => m.Tel, "myTelTemplate") @Html.DisplayForModel() @Html.DisplayForModel("myEmployeeTemplate")
@model String <span class="tel">@System.Text.RegularExpressions.Regex.Replace(Model, @"(\d{2})(\d{3})(\d{4})", "$1-$2-$3");</span>
@model Person <div>@Model.FirstName</div> <div>@Model.LastName</div>
<span class="tel">02-393-9889;</span> <span class="tel">02-393-9889;</span> <div class="display-label">編號</div> <div class="display-field">3</div> <div class="display-label">大名</div> <div class="display-field">vito</div> <div class="display-label">姓氏</div> <div class="display-field">shao</div> <div class="display-label">電話</div> <div class="display-field">023939889</div> <div class="display-label">已婚</div> <div class="display-field"><input class="check-box" disabled="disabled" type="checkbox" /></div> <div>vito</div> <div>shao</div> <div>023939889</div>
Display
使用指定的範本,傳回運算式表示的物件中每一個屬性的 HTML 標記。
public static MvcHtmlString Display(this HtmlHelper html, string expression); public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName); public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName, string htmlFieldName); public static MvcHtmlString Display(this HtmlHelper html, string expression, object additionalViewData); public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName, string htmlFieldName, object additionalViewData);
您可以使用 Display 方法顯示從 ViewData 字典和從 Model 屬性所公開之物件的資料。
@ViewData["Message"].ToString() @* 一般顯示方法 *@ @Html.Display("Message") @* 若有 Message 物件,則將其值顯示出來 *@ @Html.Label("FirstName"): @* 顯示 Employees 公開的屬性 FirstName 的欄位名稱 *@ @Html.Display("FirstName") @* 顯示 Employees 公開的屬性 FirstName 的值 *@ @Html.Label("BirthDate"): @Html.Display("BirthDate", "myDateTime") @* 使用 myDateTime 這個 Partail View ,顯示 BirthDate 物件值 *@
上面範例中的 myDateTime 是一個自訂的 partial view (user control) ,程式碼如下:
<span style="font-weight:bold;color:green;"> @Html.Encode(string.Format("{0:yyyy\\/MM\\/dd}", Model)) </span>
DisplayFor
使用指定的範本,傳回由 Expression 表示之物件中包含各個屬性值的字串。
其用法同 Display ,只是它的運算式是一個強型別物件。
public static MvcHtmlString DisplayFor(this HtmlHelper html, Expression expression); public static MvcHtmlString DisplayFor(this HtmlHelper html, Expression expression, string templateName); public static MvcHtmlString DisplayFor(this HtmlHelper html, Expression expression, string templateName, string htmlFieldName); public static MvcHtmlString DisplayFor(this HtmlHelper html, Expression expression, object additionalViewData); public static MvcHtmlString DisplayFor(this HtmlHelper html, Expression expression, string templateName, string htmlFieldName, object additionalViewData);
@Html.LabelFor(emp => emp.FirstName): @Html.DisplayFor(emp => emp.FirstName) @Html.LabelFor(emp => emp.BirthDate): @Html.DisplayFor( emp => emp.BirthDate , "myDateTime")
DisplayForModel
使用指定的範本,傳回模型中每一個屬性的 HTML 標記
public static MvcHtmlString DisplayForModel(this HtmlHelper html); public static MvcHtmlString DisplayForModel(this HtmlHelper html, object additionalViewData); public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName); public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, object additionalViewData); public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, string htmlFieldName); public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, string htmlFieldName, object additionalViewData);
1. 通常在強型別的 Veiw 中,要顯示該型別中特定的公開資料,你可以如此做:
<div>FirstName:@Html.DisplayFor(emp => emp.FirstName)</div> <div>LastName:@Html.DisplayFor(emp => emp.LastName)</div> <div>BirthDate:@Html.DisplayFor(emp => emp.BirthDate)</div>
2. 若要顯示該型別的所有公開的資料,使用直接使用 DisplayForModel 轉譯整個模型,以下範例:
@Html.DisplayForModel()
它就會自動將該 Model 中的所有公開屬性,轉譯成 html 字串,如下所示:
<div class="display-label">員工ID</div> <div class="display-field">2</div> <div class="display-label">名</div> <div class="display-field">Fuller</div> <div class="display-label">姓</div> <div class="display-field">Andrew</div> <div class="display-label">Title</div> <div class="display-field">Vice President, Sales</div> <div class="display-label">TitleOfCourtesy</div> ...
3. 你也可以將該模型資料,交由指定的範本(partial view or user control)來產出。 也就是在 user control 中設計你要的樣式,再使用這個 user control 來展現你的模型資料。 如下範例:
@Html.DisplayForModel("EmpDetail")
@model MvcApplication1.Models.Employees <div>FirstName:@Html.DisplayFor(emp => emp.FirstName)</div> <div>LastName:@Html.DisplayFor(emp => emp.LastName)</div> <div>BirthDate:@Html.DisplayFor(emp => emp.BirthDate)</div>
InputExtensions
InputExtensions represents support for HTML input controls in an application. 這個類別包含了五個擴充方法,用來建立以下五種 html 中的 input 項目:
- TextBox :
- CheckBox :
- RadioButton :
- Password :
- Hidden :
這五個擴充方法也都各有一個加了 For 結尾的擴充方法,如 TextBoxFor , CheckBoxFor , HiddenFor , PasswordFor , HiddenFor 用來接受一個運算式物件的參數。
下面這個例子,是他們在使用上的差別
@Html.TextBoxFor("Caption") @Html.TextBoxFor(model => model.Title)
<input id="title1" name="Caption" type="text" value="" /> <input id="Title" name="Title" type="text" value="Aniseed Syrup" />
EditorExtensions
EditorExtensions 底下只有一個 @Html.Editor 方法,它可以產生像 @Html.TextBox 一樣的 input control 。 除此之外,最主要的差別在 @Html.TextBox 只會產生 type="text" 屬性的 input control ,而 @Html.Editor 則會自動根據 Model 的 Property 型別,自動使用適合的 type 來建立 input control 。 還有這個方法也可搭配 templates 產出指定的樣式。
public static MvcHtmlString Editor(string expression); public static MvcHtmlString EditorFor<TModel, TValue>(Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, object additionalViewData); public static MvcHtmlString EditorForModel(string templateName, string htmlFieldName, object additionalViewData);
@Html.TextBoxFor(m => m.Merried) // Merried 是 boolean 型別 @Html.EditorFor(m => m.Merried) // Merried 是 boolean 型別
<input id="Merried" name="Merried" type="text" value="" /> <input id="Merried" name="Merried" type="checkbox" value="true" />
LinkExtensions
LinkExtensions 類別主要用來產生 HTML 的連結控制項。
ActionLink
ActionLink 方法可以用來針對 action method 建立連結控制項.
建構方法
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes);
範例1:使用 ActionLink 建立 Action 的連結
若要使用 ActionLink ,可以這樣用:
@Html.ActionLink("LinkText1", "List", "Employees"); @Html.ActionLink("LinkText2", "List", new { controller = "Employees" }); @Html.ActionLink("LinkText3", "List", new { controller = "Employees", page = "2", category ="02"}); @Html.ActionLink("LinkText4", "List", "Employees", new { area = "Admin" }, null);
上面會分別產生以下連結
<a href="/Employees/List">LinkText1</a> <a href="/Employees/List">LinkText2</a> <a href="/Employees/List?page=2&category=02">LinkText3</a> <a href="/Admin/Employees/List">Linkext4</a>
範例2:解決控制器名稱重複的問題
上面範例中,當送出 /Employees/List 需求時,因為我們在 Areas 底下也建立一個 Employees 控制器,所以系統底下共有二個同名的 Employees 控制器。 這樣子當路由在比對時,會產生以下錯誤:
要解決控制器名稱重複的問題,可以在 Routing 定義中加入控制器的命名空間,建立條件約束來區分。
routes.MapRoute( "Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, new string[] { "MvcApplication2.Controllers" } //只搜尋特定命名空間下的控制器 );
RouteLink
RouteLink 方法可以用來針對 action method, file, resource 建立連結控制項。
建構方法
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName); public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, object routeValues); public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, object routeValues);
參數說明:
- linkText:連結項目的顯示文字。
- routeName:用來傳回虛擬路徑之路由的名稱。
- routeValues:這是一個物件型別的參數,物件中必須包含用來建立 route 的參數值。 這些物件參數,會透過反映(reflection)的方法,以檢查物件中的成員,並擷取各個屬性值出來。
範例1:使用 RouteLink 建立 Action 的連結
routes.MapRoute( "DefaultA" , "{controller}/{action}/{id}" ); routes.MapRoute( "DefaultB" , "BBB/{controller}/{action}/{id}" );
上面這二個 route ,你可以透過 RouteLink 方法,產生 action 的連結。
@Html.RouteLink("LinkText4", "DefaultA"); @Html.RouteLink("LinkText5", "DefaultB"); @Html.RouteLink("LinkText6", "DefaultA", new { controller = "Employees", action = "List" }); @Html.RouteLink("LinkText7", "DefaultB", new { controller = "Employees", action = "List" });
上面會分別產生以下連結
<a href="/">LinkText4</a> <a href="/BBB">LinkText5</a> <a href="/Employees/List">LinkText6</a> <a href="/BBB/Employees/List">LinkText7</a>
範例2:ActionLink vs RouteLink
RouteLink 和 ActionLink 最大的不同之處在於,因為 Action 和 Routes 並不是一對一的關系, 當使用 ActionLink 時,系統會以 controller + action 找到第一個符合比對的路由樣式,建立連結。 若使用 RouteLink ,則可以使用指定的路由樣式,建立連結。
在前面的範例中,我們使用 EmployeeController 建立 ActionLink ,得到 /Employees/List 的輸出連結。這是因為在 RouteConfig 的定義中, DefaultA 寫在 DefaultB 上頭。 若將 DefaultB 寫在上頭,連結就會變成 BBB/Employees/List
@Html.ActionLink("LinkText8", "List", new { controller = "Employees" });
若使用 RouteLink 就可以避免這個問題。
@Html.RouteLink("LinkText9", "DefaultB", new { controller = "Employees", action = "List" });
建立 Areas 的連結
若我們使用 Areas 功能,要注意的首先是控制器重複的問題。另一個常見需求就是如何建立 Admin 區域的連結,或者如何由 Admin 區域建立外層網頁的連結。
如何使用 ActionLink 或 RouteLink 建立 Admin 區域連結?
@Html.ActionLink("AdminActionLink1", "List", "Employees", new { area = "Admin" }, null) @Html.ActionLink("AdminActionLink2", "List", new { area = "Admin", controller = "Employees" }, null) @Html.RouteLink("AdminRouteLink1", "AdminRoute", new { controller = "Employees", action = "List" }) @Html.RouteLink("AdminRouteLink2", new { area = "Admin", controller = "Employees", action = "List" })
如何由 Admin 區域連結到外層的網頁?
@Html.ActionLink("ActionLink1", "List", "Employees", new { area = "" }, null) @Html.ActionLink("ActionLink2", "List", new { area = "", controller = "Employees" }, null) @Html.RouteLink("RouteLink1", "DefaultA", new { controller = "Employees", action = "List" }) @Html.RouteLink("RouteLink2", new { area = "", controller = "Employees", action = "List" })
ChildActionExtensions
ChildActionExtensions 類別主要用來呼叫子系的 action method ,以取得 partial view 內容。
public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName); public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, object routeValues); public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName); public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues); public static void RenderAction(this HtmlHelper htmlHelper, string actionName); public static void RenderAction(this HtmlHelper htmlHelper, string actionName, object routeValues); public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName); public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues);
Action
Action 方法會叫用指定的子系的 action method 已取得回應的 partial view,並以 HTML 字串形式傳回結果。
@model MvcApplication1.Models.Customers @Html.Action("CustomerOrders", new { CustomerID = Model.CustomerID })
叫用 controller 中的 CustomerOrders 方法,並傳入參數 CustomerID 。
public ActionResult CustomerOrders(string CustomerID) { NorthwindEntities db = new NorthwindEntities(); var query = from order in db.Orders where order.CustomerID == CustomerID select order; return View(query.ToList()); }
@model IEnumerable<MvcApplication1.Models.Orders> <table> @foreach (var item in Model) { <tr> <td>@Html.DisplayFor(modelItem => item.CustomerID)</td> <td>@Html.DisplayFor(modelItem => item.OrderDate)</td> <td>@Html.DisplayFor(modelItem => item.ShipAddress)</td> <td>@Html.DisplayFor(modelItem => item.ShipCity)</td> </tr> } </table>
RenderAction
RenderAction 同 Action 功能,差別在它不傳回值,直接將轉譯的 HTML 碼輸出到 parent view。
@{ Html.RenderAction("ShowTime", "Home"); }
叫用 Home Controller 中的 ShowTime 方法。
public ActionResult ShowTime() { return View(); }
@DateTime.Now.ToString()
PartialExtensions and RenderPartialExtensions
PartialExtensions 和 RenderPartialExtensions 類別都是用來取得 partial view 內容。 和 ChildActionExtensions 差別在不會呼叫 controller 中的 action method 。
public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName); public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName);
Partial
Partial 方法會將 partial view 轉譯成 HTML 編碼字串,然後回傳字串。
@Html.Partial("CopyRightControl")
<p>Copyright © 2013 XXX Inc. All rights reserved</p>
RenderPartial
RenderPartial 同 Partial 功能,差別在它不傳回值,直接將轉譯的 HTML 碼輸出到 parent view。
@{ Html.RenderPartial("CopyRightControl"); }
ValidationExtensions
ValidationExtensions 包含以下方法:
- Validate :以文字格式回傳驗證錯誤的訊息
- ValidationMessage :以 html 格式回傳驗證錯誤的訊息
- ValidationSummary :輸出驗證錯誤訊息的清單
- ValidateFor :以 metadata 中所設定的資訊來驗證輸入的資料
- ValidationMessageFor :以 html 格式回傳驗證錯誤的訊息
在上面幾個方法中, Validate 和 ValidationMessage 主要差異在於 Validate 回傳的訊息是經過 Encode 的,也就是會先將 html 標籤轉成字碼。 而 ValidationMessage 不會,所以若訊息文字中含有 html 標籤,就會顯示出該有的樣式。 另外帶有 For 字尾的方法表示可以利用 expression 表示式來指定驗證欄位,也就是透過強型別方式來做設定;而不帶 For 結尾的方法就是只能使用字串方式來設定驗證欄位。
MVC 的 Model 驗證機制,讓我們可以只透過 Model 的 DataAnnotations 屬性設定,就可以提供用戶端和伺服器驗證檢查,而不需要撰寫其他程式碼。 這些驗證後所產生的錯誤訊息會自動存放在 ModelState 系統變數中, ModelState 是一個 ModelStateDictionary 型別物件,哪一個 Property Name 驗證有錯誤,就會建立一筆資料,並將錯誤存放在 Values.Errors 屬性,同時將原始資料保留在 Values.Value 屬性中。 你也可以在程式碼中透過 ModelState.AddModelError 方法,將自訂的錯誤訊息記錄下來,並依據自行定義的 key 值分類。
使用 ValidationMessage 或 ValidationMessageFor 時,必須指定一個 Property ,就會顯示與該 Property 相關的錯誤訊息,但只會顯示一筆。 而 ValidationSummary 則會顯示 ModelState 中的全部錯誤訊息。
[HttpPost] public ActionResult Create(Student student) { ModelState.AddModelError("keyA", "錯誤訊息A1"); ModelState.AddModelError("keyB", "錯誤訊息B1"); if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } return View(student); }
@Html.ValidationSummary("", new { @class = "text-danger" }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" }) @Html.ValidationMessageFor(model => model.FirstMidName, "", new { @class = "text-danger" }) @Html.ValidationMessageFor(model => model.EnrollmentDate, "", new { @class = "text-danger" })
清除
如果你不想錯誤訊息輸出到 View 端,你可以叫用 ModelState.Clear 方法移除全部記錄,或者 ModelState.Remove 移除特定項目。
ModelState.Clear(); ModelState.Remove("LastName");
沒有留言:
張貼留言