一
引言
本文中
我們將向你展示如何在基於ASP
NET MVC框架構建的ASP
NET應用程序中添加一些基本的Ajax特征(例如局部更新及行為組件等概念)
【說明】本文向你提供了有關於ASP
NET MVC框架的完整應用源碼及測試示例
在本文方案中
共有兩個工程
一個是TaskList(Web應用程序)
另一個是AjaxMVC(一個提供了擴展的Ajax支持的類庫)
請注意
類庫AjaxMVC中提供的函數實現了一些基本的Ajax功能
例如不依賴於頁面回寄的局部更新以及關聯到DOM元素的類似於ASP
NET AJAX框架中行為(Behavior)的擴展
實際上
最新的ASP
NET MVC框架版本(Preview
)中就已經提供了現成的Ajax支持功能
所以
你可以把這裡提供的功能作為早期ASP
NET MVC框架版本的試驗品學習
二
構建簡單任務列表示例程序
為了簡化問題的表面而專注於討論本文的主題
本文中提供了一個基本的任務列表案例應用程序
盡管此程序非常簡單
但是它卻讓我們專注於討論我們更感興趣的Ajax特征
下面給出了本文示例應用程序的一個運行時刻快照
有關MVC框架的經典入門級教程請讀者參考Scott Guthrie的博客/scottgu/archive////aspnetmvcframeworkpartaspx)我們不想在此重復這些內容但是就像Scott Guthrie提供的產品目錄應用程序一樣本文中提供的這個TaskList應用程序使用一個控制器來處理來自客戶端的請求使用一組類形成模型用於描述一個任務項的集合還有一組視圖用於生成用戶接口
首先在開始為示例添加一些Ajax功能之前我們來分析本文示例應用程序的基本組成
(一)模型
我們先來分析一下本示例的模型部分主要由下面兩部分組成
public class Task {
public int ID { get; }
public bool IsComplete { get; set; }
public string Name { get; set; }
}
public interface ITaskDB {
Task AddTask(string name);
void CompleteTask(Task task);
Task GetTask(int taskID);
ICollection<Task> GetTasks();
void RemoveTask(Task task);
}
這裡的代碼沒有多少意思但要注意的是在本例中為了簡化問題起見我直接使用了一個內存中的數據集來模擬任務列表數據(而沒有使用數據庫示例)
另外需要一提的是這裡使用了接口(ITaskDB)的方法以方便添加測試代碼(後面討論)
(二)控制器
本示例中提供了一個簡單的控制器TaskListController控制器中定義了幾個Action方法下面列出了其中的兩個方法
public class TaskListController : Controller {
private ITaskDB _taskDB;
public TaskListController() : this(GlobalTaskDB) { }
public TaskListController(ITaskDB taskDB)
{
_taskDB = taskDB;
}
//示例URL: /TaskList或者/TaskList/List
[ControllerAction]
public void List() {
Dictionary<string object> viewData = new Dictionary<string object>();
viewData[Tasks] = _taskDBGetTasks();
RenderView(List viewData);
}
//示例URL: /TaskList/Add
[ControllerAction]
public void Add(string name) {
Task newTask = null;
if (StringIsNullOrEmpty(name) == false) {
newTask = _taskDBAddTask(name);
}
if (newTask != null) {
RedirectToAction(List);
}
else {
Dictionary<string object> viewData = new Dictionary<string object>();
viewData[Tasks] = _taskDBGetTasks();
viewData[ShowAddTaskError] = true;
RenderView(List viewData);
}
}
//其他的Action方法例如DeleteTask CompleteTask
}
(三)視圖
接下來讓我們討論本示例的視圖部分本示例的視圖頁面是Listaspx定義於示例程序的/Views/TaskList文件夾下下面給出了一些令人感興趣的標記代碼
<div id=
taskList
>
<% foreach (Task task in Tasks) { %>
<div>
<div id=
taskItem<%= task
ID %>
class=
taskPanel
>
<form method=
post
action=
<% Url
Action(
CompleteTask
) %>
>
<input type=
hidden
name=
taskID
value=
<%= task
ID %>
/>
<input type=
submit
name=
completeTask
value=
Done!
/>
<input type=
submit
name=
deleteTask
value=
Delete
/>
<span><%= Html
Encode(task
Name) %></span>
</form>
</div>
</div>
<% } %>
</div>
<form method=post action=<%= UrlAction(Add) %>>
<input type=text name=name />
<input type=submit name=addTask value=Add Task />
</form>
(四)單元測試
最後我們來看一下本示例中的單元測試部分的編碼在本例中測試用例定義於一個單獨的Test工程下面給出了本示例控制器中定義的Add方法相關的一組測試
public void TestAddEmptyName() {
TestTaskDB taskDB = new TestTaskDB();
taskDB
AddTask(
Test Task
);
taskDB
AddTask(
Test Task
);
TestTaskListController controller = new TestTaskListController(taskDB);
controller
Add(null);
Assert
AreEqual(
List
controller
RenderedView);
Assert
AreEqual(true
controller
GetRenderedViewData(
ShowAddTaskError
));
Assert
AreEqual(
taskDB
Count);
}
[TestMethod]
public void TestAddValidName() {
TestTaskDB taskDB = new TestTaskDB();
taskDBAddTask(Test Task );
taskDBAddTask(Test Task );
TestTaskListController controller = new TestTaskListController(taskDB);
controllerAdd(New Task);
AssertAreEqual(List controllerRedirectedView);
AssertAreEqual( taskDBCount);
}
好至此我已經較完整地向你介紹了本文中編寫的簡單的TaskList應用程序接下來讓我們討論如何在這個示例中添加一些Ajax支持功能下面列出了我們計劃要添加的一些功能
在任務列表中添加新任務(位於列表的最後)而不必進行完整的頁面回送
編輯和刪除任務同樣不必進行完整的頁面回送
在文本框中添加水印效果
為了實現上面的既定目標和最大限度地把新添加的功能與原有ASPNET MVC框架融合到一起我們使用了SystemWebMvc命名空間中新增加的一些類例如Ajax擴展方法等
三在控制器中加入AJAX支持技術
在此我們要做的第一件事情是從AjaxController類(而不是直接從Controller類)中派生類TaskListController
AjaxController是我剛剛添加的一個類此類引入了一個新的屬性IsAjaxRequest我在自己的Action方法中就使用這個屬性來完成諸如生成不同視圖之類的任務此外它還引入了一些成員函數例如RenderPartial這個RenderPartial函數可以用於生成定義在一個部分視圖中的用戶接口的一部分下面給出修改後的控制器以及新修改的Add方法(其中修改部分及添加部分均以粗體顯示)
public class TaskListController : AjaxController {
public void Add(string name) {
Task newTask = null;
if (StringIsNullOrEmpty(name) == false) {
newTask = _taskDBAddTask(name);
}
if (IsAjaxRequest) {
if (newTask != null) {
RenderPartial(TaskView newTask);
}
}
else {
if (newTask != null) {
RedirectToAction(List);
}
else {
Dictionary<string object> viewData = new Dictionary<string object>();
viewData[Tasks] = _taskDBGetTasks();
viewData[ShowAddTaskError] = true;
RenderView(List viewData);
}
}
}
}
接下來我把TaskView重新定義為一個自定義控件TaskViewascx(位於/Views/TaskList文件夾下)代碼如下所示
<div id=
taskItem<%= Task
ID %>
class=
taskPanel
>
<form method=
post
action=
<%= Url
Action(
CompleteTask
) %>
>
<input type=
hidden
name=
taskID
value=
<%= Task
ID %>
/>
<input type=
submit
name=
completeTask
value=
Done!
/>
<input type=
submit
name=
deleteTask
value=
Delete
/>
<span><%= Html
Encode(task
Name) %></span>
</form>
</div>
其實上面的代碼僅僅是前面介紹的Listaspx的簡單重構(所以你可能看上去十分熟悉這段代碼)相應於這個用戶控件的ViewData是一個Task類的實例現在既然我們已經定義了這一部分那麼接下來我們就可以從本例主要的視圖頁面Listaspx中使用它了這一點是借助於前面我提供的RenderPartial擴展方法實現的一旦做到這些視圖Listaspx的任務列表部分將變為
<div id=
taskList
>
<% foreach (Task task in Tasks) { %>
<div>
<% this
RenderPartial(
TaskView
task); %>
</div>
<% } %>
</div>
接下來我需要讓此視圖發出XMLHttp請求而不是一個傳統的表單提交再次我提供了一些擴展方法
⑴RenderBeginForm描述的是一個普通的表單標簽
⑵RenderBeginAjaxForm將負責生成一個支持AJAX功能的表單(這正是我們的興趣點所在)
⑶RenderEndForm
借助於這些方法實現添加任務的UI表單標簽部分看上去如下加粗部分所示
<% RenderBeginAjaxForm(Url
Action(
Add
)
new { Update=
taskList
UpdateType=
appendBottom
Highlight=
True
Starting=
startAddTask
Completed=
endAddTask
}); %>
<input type=
text
name=
name
/>
<input type=
submit
name=
addTask
value=
Add Task
/>
<% RenderEndForm(); %>
如你所見表單的內容並沒有發生變化僅僅是聲明的形式發生了變化在上面的代碼中RenderBeginAjaxForm接收當提交表單時描述要調用的行為的URL後面跟著的是如下的一些Ajax特定參數
; Update此參數相應於使用結果進行更新的DOM 元素的id值在本例中它對應於描述存放所有任務項的容器
; UpdateType此參數取值可以為nonereplacereplaceContentinsertTop或者appendBottom—在上面的例子中我們給它的賦值是最後面的值appendBottom此值將使得新渲染生成的任務顯示於整個任務列表的底部
; Highlight此參數是可選的當設置此參數時新添加的項將會高亮顯示一會兒呈現微微帶點黃色的漸隱效果
; Starting和Completed這兩個參數實質上都是事件我們可以編寫一段Javascript代碼實現例如禁用按鈕顯示進度指示器 在發出的請求中添加額外內容或預處理到來的響應等等
下面是Javascript代碼(位於文件TaskListjs中此文件位於示例程序的/Views/Scripts文件夾下)
function startAddTask() {
$(
addTaskGroup
)
disabled = true;
return true;
}
function endAddTask() {
$(
addTaskGroup
)
disabled = false;
return true;
}
在上面的startAddTask方法中我們進行了校驗操作在此請注意如果相應的形式無效那麼為了避免在這樣情況下也發出請求需要返回false注意這裡的代碼僅僅展示了一些基本形式的校驗編碼
最後一步是添加進腳本TaskList以及提供相應核心功能的腳本框架現在我們來打開位於文件夾/Views/Layouts下的示例程序的母版頁面然後添加一些指令以初始化Ajax功能注冊腳本並在最終生成的HTML代碼的最後輸出腳本這是通過調用我添加到Ajax對象中的擴展方法實現的
<% Ajax
Initialize(); %>
<% Ajax
RegisterScript(
~/Views/Scripts/TaskList
js
); %>
<!—UI部分定義在此
>
<% Ajax
RenderScripts(); %>
實際上我要實現的另一項任務就是添加一個測試用例於是我添加一個測試用例用於測試我的控制器中Ajax化的Action方法Add
接下來我們可以針對完成和刪除兩個任務添加同樣相似的測試用例其中完成任務將導致相應於此任務的UI重新生成使用新的HTML代替現有的HTML而相比之下刪除任務更為有趣些不是更新HTML而是原有內容從DOM結構中移除改以使用HighlightLeave 效果(一種紅色漸隱效果)造成視覺上更為引人注目你可以進一步分析本示例源碼來了解其中的原理(特別是文件TaskListjs和TaskViewascx以及相關聯的控制器中的Action方法)
四添加其他的AJAX技術
我們完全可以實現類似於包含在TaskViewascx中的<form>部分而且我們同樣可以其中描述每一個任務項但是卻能夠把一個常規的基於提交的表單轉換成一個支持AJAX技術的表單這樣以來任務項的編輯與刪除操作就可以在局部刷新狀態下實現示例代碼中對此作了解釋在此不再贅述
接下來我想介紹的是如何添加一些腳本並把它添加到我們的示例程序的UI中創建其他基於AJAX的交互而生成的HTML具體地說我想在文本框中添加一個水印效果此效果為用戶輸入提供了極為友好的用戶直觀性提示只要沒有用戶輸入此水印效果就會顯示出來而當用戶把輸入焦點定位於文本框中時即水印效果消失
當然篇幅所限我們也不會過於細致地去討論腳本本身有關此腳本詳細內容請參考本文源碼但是需要指出的是這個水印效果被實現為大家可能熟悉的ASPNET AJAX框架的一個客戶端行為(Behavior)組件就像任何其他行為組件一樣我們的示例中所使用的文本框也是與DOM元素相關聯而且它實現了對此元素引發的相關事件的訂閱
在傳統的web表單頁面中我經常會直接使用支持AJAX功能的服務器控件例如WatermarkExtender並使之關聯到一個服務器控件但是在本例中我使用了另一種擴展方法來實現渲染效果通過此方法我也可以實現創建並初始化腳本行為組件的一個實例下面給出了我更新以後的視圖關鍵部分的代碼片斷
<% RenderBeginAjaxForm(Url
Action(
Add
)
new { Update=
taskList
UpdateType=
appendBottom
Highlight=
True
Starting=
startAddTask
Completed=
endAddTask
}); %>
<input type=
text
name=
name
id=
nameTextBox
/>
<% Ajax
Watermark(
nameTextBox
new { watermarkText=
[What do you need to do?]
watermarkCssClass=
watermark
}); %>
<input type=submit name=addTask value=Add Task />
<% RenderEndForm(); %>
上面的擴展方法實現相當簡單其實它也就是調用了現成的Ajax框架下面是我定義的WatermarkBehavior類相應的代碼
public static class WatermarkBehavior {
public static void Watermark(this AjaxHelper ajaxHelper
string id
object watermarkOptions) {
ajaxHelper
RegisterScript(
~/Views/Scripts/Watermark
js
);
ajaxHelper
RegisterScriptBehavior(id
Ajax
Watermark
watermarkOptions);
}
}
當然我們還可以更細致地控制上面的編碼但這裡僅展示了提供搜集注冊的腳本功能核心部分的代碼片斷以及把它們生成到頁面中然後實例化行為對象並使其與相應的DOM元素建立關聯以及傳遞進視圖提供的選擇以便定制具體的實例
五結論
歸納來看本文也只不過是蹭了蹭基於MVC框架進行ASPNET頁面編程中所涉及的局部更新行為和擴展器控件等Ajax功能的核心方面我相信除了上面這兩種情況外還有大量的其他內容需要進行Ajax化(校驗同期性刷新通過腳本代理以及web服務等技術進一步簡化調用控制器方法等)最後讀者可以詳細研讀我提供的示例代碼並給予相應的改進
From:http://tw.wingwit.com/Article/program/net/201311/13592.html