前言
為了這一篇,前面需要鋪了許多的梗,寫了許多可能不是很重要但很基礎的觀念,包含
- Html 基礎 (請參考 網頁系統 與 Html基礎簡介)
- Controller, Action 與 View 的關係(請參考 Controller and View 1-3)。
- Razor, html Helper, 與model binder (請參考 Razor and Htmlhelper)
當對於前面幾篇文章稍微有概念後,看到這篇才不會有太多的疑問。若有觀念錯誤與修改建議,也請各位先進不吝指教。
介紹
在Controller and View 2 - 資料傳遞篇,我們有提到簡易的接收與傳遞資料的方法,但隨
著系統功能複雜與欄位越來越多的情況下,我們的資料內容也越來越多,而必須增加許
多程式碼做介接與驗證,維護變得相當不容易。
一般來說,許多程式開發者會使用(Data transfer object, DTO)的方式來處理這個問題。
將所有的參數放入一個物件裡,進行傳遞,好處是增加程式的可讀性與容易使用。
回想起剛開始學習 MVC 的時候,不懂ViewModel的目的與用途,而一直將他視作DTO
並用來傳遞參數,但隨著開發時間久了,雖然覺得ViewModel與DTO頗相似,但個人覺
得比ViewModel 較接近於描述 View 的行為或定義 View 的規格。
簡單的例子:
使用者資料維護功能,CreateUserViewModel 與 UpdateUserPasswordViewModel 內容來
源都是使用者資料庫,但 UpdateUserPasswordViewModel 內可能只有使用者帳號與密碼
相關欄位,而不需要將所有資料帶出。
另外,ViewModel也能透過增加屬性的方式,進行資料格式指定與驗證,所以個人認知
偏向 ViewModel 用於描述 View 的規格。
要如何設計一個ViewModel,對於第一次建立的同學,我們建議兩個方法:
- 資料長怎樣,就怎麼設計。
- View 需要什麼資料,就怎麼設計。
以學生資料為例,我們需要下列資料:
- 學號
- 姓名
- 班級
- 通訊地址
- 電子郵件
- 連絡電話
首先,先右鍵選取 Project -> add -> add folder ,命名為ViewModel
在 ViewModel 資料夾點選右鍵 -> Add item...
選擇Code -> Class,取名為CreateStudentViewModel.cs,點選Add
修改 CreateStudentViewModel.cs 如以下程式:
namespace WebApplication1.ViewModel { public class CreateStudentViewModel { public string ID { get; set; } public string NAME { get; set; } public string ADDRESS { get; set; } public string EMAIL { get; set; } public string TEL { get; set; } } }
我們在HomeController,建立兩個Create action,如下所示:
public ActionResult Create() { CreateStudentViewModel model = new CreateStudentViewModel(); return View(model); } [HttpPost] public ActionResult Create(CreateStudentViewModel model) { return View(model); }
第一個 Public Action Result Create()
使用者第一次進入學生資料建立畫面的時候,不會帶入任何參數。但我們必須初始化
CreateStudentViewModel,並且傳遞進入View提供使用。(若不初始化,View 使用
model binder取得Model資料時,會產生null exception)
第二個public ActionResult Create(CreateStudentViewModel model)
當使用者輸入資料後點選送出,我們接收CreateStudentViewModel的資料,進行邏輯運算。
(可能是驗證、新增資料或計算動作...等)
接下來,我們對 Create action 點選右鍵 -> Add View... 建立View
在 View 最上方,我們要先宣告ViewModel的格式
@model WebApplication1.ViewModel.CreateStudentViewModel
我們將 View 內容改成如下
@model WebApplication1.ViewModel.CreateStudentViewModel @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.AntiForgeryToken() @Html.ValidationSummary(true, "", new { }) <table> <tr> <th>@Html.LabelFor(model => model.ID)</th> <td> @Html.TextBoxFor(model => model.ID) @Html.ValidationMessageFor(model => model.ID, "", new {}) </td> </tr> <tr> <th>@Html.LabelFor(model => model.NAME)</th> <td> @Html.TextBoxFor(model => model.NAME) @Html.ValidationMessageFor(model => model.NAME, "", new { }) </td> </tr> <tr> <th>@Html.LabelFor(model => model.EMAIL)</th> <td> @Html.TextBoxFor(model => model.EMAIL) @Html.ValidationMessageFor(model => model.EMAIL, "", new { }) </td> </tr> <tr> <th>@Html.LabelFor(model => model.TEL)</th> <td> @Html.TextBoxFor(model => model.TEL) @Html.ValidationMessageFor(model => model.TEL, "", new { }) </td> </tr> <tr> <th>@Html.LabelFor(model => model.ADDRESS)</th> <td> @Html.TextBoxFor(model => model.ADDRESS) @Html.ValidationMessageFor(model => model.ADDRESS, "", new { }) </td> </tr> <tr> <td><input type="submit"></td> <td></td> </tr> </table> }
執行程式後,網址輸入localhost:xxxx/Home/Create,可以看到下列畫面。 因為@Html.LabelFor() 沒有給予名稱,所以直接呈現binding ViewModel 內的變數名稱。
接著,我們可以在ViewModel 先增加using System.CompinentModel,接著在每一個變數上 設定 DisplayName 屬性,在View 的 @Html.LabelFor 即可以顯示欄位名稱。
我們可以在Controller設定中斷點並執行程式後,於網頁上輸入資料並送出,即可檢視資料 內容,確認是否有做到model binder的效果。
我們能增加 Model 驗證與驗證描述,當使用者違反欄位限制的時候,提示使用者訊息。 下列這些語法能幫助我們:
@Html.AntiForgeryToken() @Html.ValidationSummary(true, "", new { }) @Html.ValidationMessageFor()
我們能在 ViewModel 增加 Reqired Annotations,讓這欄位必須輸入: 我們能在 ViewModel 增加電子郵件驗證,檢查是否符合電子郵件格式: 我們能在 ViewModel 增加正規表達式驗證,讓這個欄位符合條件:
這篇只簡單介紹幾個應用,還有許多屬性可以使用,就讓同學們自行去發現了。
範例
https://dl.dropboxusercontent.com/u/13585157/ViewModelExample.zip
感想
回到學校教導學弟妹專題,很開心的直接分享程式內容,直接對 ViewModel進行程式講 解,才發現一堆學弟妹完全不知道我在講什麼。立刻回頭更改教學內容,從基礎的內容 講解起,但也已經難以挽回,效果不彰。運氣很好,還有第二次教學弟妹的機會,將教 材順序做了調整,而這一批的學習成效,比起第一批學弟妹,效果好很多。 教導學生教材與自己學習的內容筆記是完全兩碼子的事情,要寫出簡顯易懂的程式教學 ,是非常困難的事情,而這也是我目前不斷努力改進的地方。
本篇文章內容歡迎分享,轉載與使用圖文請來信告知並註明出處。