JavaScriptMVC的 controllers 可以做什麼事情,他是一個jquery外掛程式工廠。他們可以被當成傳統視圖使用,製作分頁控制項或者grid,也可以把他們當成傳統的控制器使用,初始化控制器並把他們和model結合。Controller對組織應用程式相當有協助。
Controller提供了很多相當有用的特性,例如:
- jQuery 外掛程式建立
- 自動綁定
- 預設設定
- 自動決定
下面的代碼建立了一個tooltip控制項,當頁面被點擊的時候元素將會顯示:
$.fn.tooltip = function(){ var el = this[0]; $(document).click(function(ev){ if(ev.target !== el){ $(el).remove() } }) $(el).show(); return this;})
要使用這個控制項,你需要在頁面上添加一個元素,然後在他上面使用tooltip:
$("<div class='tooltip'>Some Info</div>") .appendTo(document.body) .tooltip()
但是上面的代碼存在問題,那就是會有很多tooltip元素會被建立,他們會被儲存在記憶體中佔用很多內容,這會導致效能問題。這是因為click的處理函數不會從頁面中移除,會有個閉包一直指向這些元素。
而且這也很容易造成錯誤。當html元素從頁面上移出的時候,jquery會移出他相關的事件處理,所以開發人員不用擔心取消事件綁定。但是對於元素之外的事件處理,例如document上的,並沒有取消事件綁定。在MVC架構中,Controller監聽View,View監聽Model。你會一直監聽控制項元素外面的事件,例如之前章節中講到的nextPrev外掛程式,會監聽paginate模型的更新操作。
paginate.bind('updated.attr', function(){ self.find('.prev')[this.canPrev() ? 'addClass' : 'removeClass']('enabled') self.find('.next')[this.canNext() ? 'addClass' : 'removeClass']('enabled');})
但是他們並不從paginate中取消綁定,忘記刪除事件處理常式是一個潛在的錯誤的來源。然而tooltip 和 nextPrev都不會發生錯誤,但是他們會默默的消耗應用程式的效能。幸運的是,$.Controller讓工作邊的簡單而易管理,我們可以像下面這樣重寫tooltip:
$.Controller('Tooltip',{ init: function(){ this.element.show() }, "{document} click": function(el, ev){ if(ev.target !== this.element[0]){ this.element.remove() } }})
當document被點擊或者元素從DOM移出,$.Controller會自動取消對document click事件的綁定。$.Controller也綁定nextPrev控制項的paginate模型:
$.Controller('Nextprev',{ ".next click" : function(){ var paginate = this.options.paginate; paginate.attr('offset', paginate.offset+paginate.limit); }, ".prev click" : function(){ var paginate = this.options.paginate; paginate.attr('offset', paginate.offset-paginate.limit ); }, "{paginate} updated.attr" : function(ev, paginate){ this.find('.prev')[paginate.canPrev() ? 'addClass' : 'removeClass']('enabled') this.find('.next')[paginate.canNext() ? 'addClass' : 'removeClass']('enabled'); }})// 建立nextprev$('#pagebuttons').nextprev({ paginate: new Paginate() })
如果#pagebuttons從頁面中移出,Nextprev控制項執行個體會自動取消對paginate模型的綁定。接下來我們討論$.Controller時如何工作的。
概述
$.Controller繼承自$.Class,使用$.Controller( NAME, classProperties, instanceProperties )去建立Controller類,參數包括控制項名稱,靜態方法和執行個體方法。下面我們開始建立一個可以重用的List控制項:
$.Controller("List", { defaults : {}},{ init : function(){ }, "li click" : function(){ }})
當一個controller類被建立的時候,他就建立了一個和類名字類似的jquery輔助方法。輔助方法主要用於在頁面上的html元素建立controller的執行個體。輔助方法的名字會用底線代替類名中的部分字元,例如$.Controller('App.FooBar')的輔助方法名是$(el).appfoobar()。
控制器執行個體化
可以使用new Controller(element, options)建立controller的執行個體,方法包含兩個參數:使用控制項的html元素和可選配置項,例如:
new List($('ul#tasks'), {model : Task});
當然你也可以使用jquery輔助方法建立類的執行個體:
$('ul#tasks').list({model : Task})
當controller執行個體被建立的時候,會調用到原型方法init:
this.element 指向controller所在的html元素;
this.options 將傳遞給建構函式的options和預設的options合并。
下面的代碼從model擷取task的資料用以更新控制器,並且使用options的模板渲染頁面:
$.Controller("List", { defaults : { template: "items.ejs" }},{ init : function(){ this.element.html( this.options.template, this.options.model.findAll() ); }, "li click" : function(){ }})
我們可以像這樣使用List控制器,代碼相當的簡單,如果我們不提供模板,List會預設使用"items.ejs"。
$('#tasks').list({model: Task, template: "tasks.ejs"});$('#users').list({model: User, template: "users.ejs"})
事件綁定
$.Controller一個強大的特性是對事件的綁定和取消綁定。當controller被建立的時候,他會查詢action方法。action方法看起來像事件處理方法,例如"li click"。那些action使用jQuery.bind 或者jQuery.delegate實現綁定。當controller被銷毀的時候,controller所在的html元素會從頁面中移出,或者使用controller的destroy方法,那些事件會取消綁定,以節約記憶體。那面是action的例子,描述了他們監聽什麼對象:
"li click" - controller所在元素中的li或li內部元素被click。
"mousemove" - controller所在元素執行mousemoves。
"{window} click" - window或內部對象被click。
action方法會被所在的html元素或者發生事件的對象回調,例如:
"li click": function( el, ev ) { assertEqual(el[0].nodeName, "li" ) assertEqual(ev.type, "click")}
action模板化
$.Controller支援action模板化,可以用來綁定其他對象、自訂事件或者自訂選取器。$.Controller使用options的值或者window來代替action,例如下面的例子:
$.Controller("Menu",{ "li {openEvent}" : function(){ // 事件處理 }});//建立 menu,設定li的click事件$("#clickMenu").menu({openEvent: 'click'});//建立 menu,設定li的mouseenter事件$("#hoverMenu").menu({openEvent: 'mouseenter'});
我們可以進一步對元素的標籤實現自訂的功能:
$.Controller("Menu",{ defaults : {menuTag : "li"}},{ "{menuTag} {openEvent}" : function(){ // 事件處理}});
$("#divMenu").menu({menuTag : "div", openEvent: 'click'})
action模板化允許你對controller之外的元素或者對象進行綁定。例如之前章節中的Task模型,他建立的過程中會觸發“created”事件。我們可以為List添加對Task created 事件的監聽,如下:
$.Controller("List", { defaults : { template: "items.ejs" }},{ init : function(){ this.element.html( this.options.template, this.options.model.findAll() ); }, "{Task} created" : function(Task, ev, newTask){ this.element.append(this.options.template, [newTask]) }})
我們也可以把model模型化,這樣可以通過在options中設定model,實現對任意model的支援:
$.Controller("List", { defaults : { template: "items.ejs", model: null }},{ init : function(){ this.element.html( this.options.template, this.options.model.findAll() ); }, "{model} created" : function(Model, ev, newItem){ this.element.append(this.options.template, [newItem]) }});// 建立 tasks list$('#tasks').list({model: Task, template: "tasks.ejs"});
集合所有功能--一個抽象的CRUD列表
現在我們為List添加對update和destroyed的支援:
"{model} updated" : function(Model, ev, updatedItem){ // 使用updatedItem更新 LI},"{model} destroyed" : function(Model, ev, destroyedItem){ // 使用destroyedItem移出 LI}
我們會發現一個問題,需要需要找到代表特定model執行個體的元素,因此我們需要把元素標記為某個mode執行個體所有。幸運的是,$.Model 和 $.View讓上述的工作變得簡單。在ejs視圖中使用model執行個體標記元素,你只需要簡單的把model執行個體寫給元素。
<% for(var i =0 ; i < this.length; i++){ %> <% var task = this[i]; %> <li <%= task %> > <%= task.name %> </li><% } %>
tasks.ejs遍曆task列表,為每一個task建立一個li,同時使用 <li <%= task %> >為task到元素的jquery資料中。可以使用modelInstance.elements([CONTEXT]),稍後通過model執行個體獲得代表他的html元素。
$.Controller("List", { defaults : { template: "items.ejs", model: null }},{ init : function(){ this.element.html( this.options.template, this.options.model.findAll() ); }, "{model} created" : function(Model, ev, newItem){ this.element.append(this.options.template, [newItem]) }, "{model} updated" : function(Model, ev, updatedItem){ updatedItem.elements(this.element) .replaceWith(this.options.template, [updatedItem]) }, "{model} destroyed" : function(Model, ev, destroyedItem){ destroyedItem.elements(this.element) .remove() }});// 建立tasks list$('#tasks').list({model: Task, template: "tasks.ejs"});
通過上面的介紹,你一定掌握了如何使用JavaScriptMVC建立抽象的、重用的和節省記憶體的外掛程式了。
javascriptMVC教程目錄