javascriptMVC教程 — 8.開始使用jQueryMX–Model

來源:互聯網
上載者:User

  JavaScriptMVC的Model的相關外掛程式提供了很多工具用來更好的組織模型資料,例如驗證、關聯、列表等。但是更核心的功能是服務封裝、類型轉換和事件。

 

  屬性和可觀察性

  對於模型層最重要的就是擷取和設定模型的屬性,還有就是監聽模型執行個體得變化。可以使用觀察者模式解決這一問題,MVC使用視圖去監視模型的改變。

  javascriptMVC很容易實現這種可觀察性。一個很好的例子就是分頁,在一個頁面中經常存在好幾個分頁控制項,一個控制項提供上一頁和下一頁的按鈕,另一個控制項提供當前頁顯示的詳細資料(例如:"正在顯示 1-20 條"),所有控制項都需要完全相同的資料:

  • offset - 顯示的第一條資料的索引
  • limit - 每頁資訊顯示數
  • count - 資訊總條數

我們可以使用$.Model建立這個模型資料:

var paginate = new $.Model({  offset: 0,  limit: 20,  count: 200});

  現在paginate變數就可以被查看了。可以把它傳遞給那些對他執行讀寫操作,或者監視其屬性變化的分頁控制項。你可以使用傳統方式或者使用model.attr(Name)方法訪問paginate的屬性:

assertEqual( paginate.offset, 0 );assertEqual( paginate.attr('limit') , 20 );

  當我們點擊下一頁的時候需要增加offset的值,可以使用model.attr(NAME, VALUE)改變屬性值。下面的代碼實現翻頁:

paginate.attr('offset',20); 

當paginate的狀態被一個控制項修改的時候,另外的控制項需要被通知到。你可以使用model.bind(ATTR, success( ev, newVal ) )綁定特性屬性的變化,然後更新控制項:

paginate.bind('offset', function(ev, newVal){  $('#details').text( 'Showing items ' + (newVal+1 )+ '-' + this.count )})

你也可以綁定'updated.attr'來監聽任何屬性的變化:

paginate.bind('updated.attr', function(ev, newVal){  $('#details').text( 'Showing items ' + (newVal+1 )+ '-' + this.count )})

  下面的代碼是接受paginate資料的next-previous外掛程式的代碼:

$.fn.nextPrev = function(paginate){   this.delegate('.next','click', function(){     var nextOffset = paginate.offset+paginate.limit;     if( nextOffset < paginate.count){       paginate.attr('offset', nextOffset );     }   })   this.delegate('.prev','click', function(){     var nextOffset = paginate.offset-paginate.limit;     if( 0 < paginate.offset ){       paginate.attr('offset', Math.max(0, nextOffset) );     }   });   var self = this;   paginate.bind('updated.attr', function(){     var next = self.find('.next'),         prev = self.find('.prev');     if( this.offset == 0 ){       prev.removeClass('enabled');     } else {        prev.removeClass('disabled');     }     if( this.offset > this.count - this.limit ){       next.removeClass('enabled');     } else {        next.removeClass('disabled');     }   })};

  這個外掛程式也存在一些問題,首先如果控制項從頁面上移除,他不能從paginate上取消綁定。我們會在Controller章節中討論。其次存在判斷offset,防止他為負數或超過最大值的邏輯,這塊邏輯應該放在model中。我們需要添加其他的判斷,限制limit,offset。我們需要建立一個pagination類。

 

  擴充Model

  JavascriptMVC的Model繼承自$.Class,我們建立一個model類,通過繼承$.Model(NAME, [STATIC,] PROTOTYPE)的方式實現:

$.Model('Paginate',{  staticProperty: 'foo'},{  prototypeProperty: 'bar'})

  有很多方式能讓Paginate模型變得更有用,首先可以添加setter方法,可以限制offset、count的值。

 

  Setters

  setter方法是model的原型方法,他被命名為setNAME,他有三個參數:val(被傳遞給model.attr(NAME, val)),success和error回呼函數。一般而言,方法返回要設定給model執行個體的值,或者當異常發生的時候執行error方法。當執行非同步setter的時候,執行success方法。下面的例子添加了setCount和setOffset執行個體方法,並且防止屬性被設定為負數。

$.Model('Paginate',{  setCount : function(newCount, success, error){    return newCount < 0 ? 0 : newCount;  },  setOffset : function(newOffset, success, error){    return newOffset < 0 ? 0 : Math.min(newOffset, !isNaN(this.count - 1) ? this.count : Infinity )  }})

現在nextPrev外掛程式可是使用下面的方式設定offset的值:

this.delegate('.next','click', function(){  paginate.attr('offset', paginate.offset+paginate.limit);})this.delegate('.prev','click', function(){    paginate.attr('offset', paginate.offset-paginate.limit );});

 

  預設值

  我們可以設定靜態defaults屬性來為Paginate的執行個體設定預設值,當新執行個體被建立的時候,如果沒有設定其他值,執行個體將會使用預設值。

$.Model('Paginate',{  defaults : {    count: Infinity,    offset: 0,    limit: 100  }},{  setCount : function(newCount, success, error){ ... },  setOffset : function(newOffset, success, error){ ... }})var paginate = new Paginate({count: 500});assertEqual(paginate.limit, 100);assertEqual(paginate.count, 500);

  現在Paginate已經很有型了,但是通過添加輔助方法他會變得更棒。

 

  輔助方法

  輔助方法是原型方法,他用來在執行個體上擷取或者設定有用的資料。下面的例子中,Paginate包含了next和prev方法,用來導航到不同的頁面。他還提供了canNext和canPrev方法,用來判斷是否可以翻頁。

$.Model('Paginate',{  defaults : {    count: Infinity,    offset: 0,    limit: 100  }},{  setCount : function( newCount ){    return Math.max(0, newCount  );  },  setOffset : function( newOffset ){    return Math.max( 0 , Math.min(newOffset, this.count ) )  },  next : function(){    this.attr('offset', this.offset+this.limit);  },  prev : function(){    this.attr('offset', this.offset - this.limit )  },  canNext : function(){    return this.offset > this.count - this.limit  },  canPrev : function(){    return this.offset > 0  }})

  現在我們的jquery外掛程式越來越有型了。

$.fn.nextPrev = function(paginate){   this.delegate('.next','click', function(){     paginate.attr('offset', paginate.offset+paginate.limit);   })   this.delegate('.prev','click', function(){     paginate.attr('offset', paginate.offset-paginate.limit );   });   var self = this;   paginate.bind('updated.attr', function(){     self.find('.prev')[paginate.canPrev() ? 'addClass' : 'removeClass']('enabled')     self.find('.next')[paginate.canNext() ? 'addClass' : 'removeClass']('enabled');   })};

 

  服務封裝

  我們已經看到了$.Model對於模型化用戶端狀態時多麼有用,但是對於大多數程式來說,資料都在伺服器端,而不在用戶端。用戶端需要create, retrieve, update 和 delete (CRUD) 伺服器上的資料。維護用戶端和伺服器端的資料是件棘手的事情,而$.Model可以很好的解決這個問題。$.Model相當的靈活,他可以處理任意服務類型和資料類型,我們這裡只介紹具象狀態傳輸(REST) 和 JSON。

  REST服務使用urls 和 HTTP 傳輸協議 POST, GET, PUT, DELETE分別實現對資料的create, retrieve, update, 和 delete。例如一個允許你create, retrieve, update 和delete任務的任務服務看起來會是這樣:

動作 動詞(verb) URL BODY 響應
Create a task POST /tasks name=do the dishes
{  "id"       : 2,  "name"     : "do the dishes",  "acl"      : "rw" ,  "createdAt": 1303173531164 // April 18 2011}
Get a task GET /task/2  
{  "id"       : 2,  "name"     : "do the dishes",  "acl"      : "rw" ,  "createdAt": 1303173531164 // April 18 2011}
Get tasks GET /tasks  
[{  "id"       : 1,  "name"     : "take out trash",  "acl"      : "r",  "createdAt": 1303000731164 // April 16 2011},{  "id"       : 2,  "name"     : "do the dishes",  "acl"      : "rw" ,  "createdAt": 1303173531164 // April 18 2011}]
Update a task PUT /task/2 name=take out recycling
{  "id"       : 2,  "name"     : "take out recycling",  "acl"      : "rw" ,  "createdAt": 1303173531164 // April 18 2011}
Delete a task DELETE /task/2  
{}
 

 

  下面的代碼串連到伺服器,允許我們在伺服器上create, retrieve, update 和 delete任務。

$.Model("Task",{  create  : "POST /tasks.json",  findOne : "GET /tasks/{id}.json",  findAll : "GET /tasks.json",  update  : "PUT /tasks/{id}.json",  destroy : "DELETE /tasks/{id}.json"},{ });

  下面的代碼展示了,我們如何使用task去CRUD任務。

動作 代碼 描述
Create a task
new Task({ name: 'do the dishes'})  .save(     success( task, data ),     error( jqXHR)   ) -> taskDeferred

在伺服器端建立model執行個體, 首先使用 new Model(attributes). 然後執行 save().

Save的時候判斷 task 是否包含 id. 在這個例子中,我們不使用task的屬性去建立請求儲存資料,我們給 Save提供兩個參數:

  • success - 執行成功時執行,從伺服器擷取到task執行個體和資料。
  • error - 發生異常時執行。

Save 返回一個延時來解決任務建立。

Get a task
Task.findOne(params,   success( task ),   error( jqXHR) ) -> taskDeferred
從伺服器擷取單個執行個體,提供三個參數:

  • params - 傳遞給伺服器的資料,例如:{id: 2}.
  • success - 執行成功時執行,從伺服器擷取到task執行個體和資料。
  • error - 發生異常時執行。

findOne 返回延時解決task。

Get tasks
Task.findAll(params,   success( tasks ),   error( jqXHR) ) -> tasksDeferred
從伺服器返回task數組,採用三個參數:

  • params - data to pass to the server. Typically, it's an empty object ({}) or filters:{limit: 20, offset: 100}.
  • success - 執行成功時執行,從伺服器擷取到task執行個體和資料。
  • error - 發生異常時執行。

findOne 返回延時解決tasks數組。

Update a task
task.attr('name','take out recycling');task.save(   success( task, data ),   error( jqXHR) ) -> taskDeferred

在伺服器上執行更新, 首先修改模型執行個體的attr,然後執行save()。Save使用相同的參數,返回與建立task相同的延時。

Destroy a task
task.destroy(   success( task, data ),   error( jqXHR) ) -> taskDeferred

從伺服器銷毀task,採用兩個參數:

  • success - 執行成功時執行,從伺服器擷取到task執行個體和資料。
  • error - 發生異常時執行。

Destroy 返回延時用於銷毀task。

  Task模型變成了我們服務的契約。

 

  類型轉換

  Javascript編程的時候經常會遇到時間轉換的難題,例如1303173531164代表的是April 18th, 2011,我們需要使用new Date(1303173531164)的方式來解決問題。幸運的是,$.Model為我們提供了類型轉換的功能,可以很方便的解決這個問題。我們需要定義一個類型的屬性和相應的轉換函式,類型屬性放在靜態對象attributes中,轉換函式放在靜態對象convert中,如下:

$.Model('Task',{  attributes : {    createdAt : 'date'  },  convert : {    date : function(date){      return typeof date == 'number' ? new Date(date) : date;    }  }},{});

   我們可以使用下面的方式使用:

Task.findAll({}, function(tasks){  $.each(tasks, function(){    console.log( "Year = "+this.createdAt.fullYear() )  })});

 

  CRUD事件

  當執行個體被created, updated, 或 destroyed的時候,模型會發布事件。你可以在Model上或者在他的執行個體上監聽事件,使用MODEL.bind(EVENT, callback( ev, instance ) )去監聽created, updated, 或 destroyed事件。當我們想知道task被建立和添加到頁面上,或者他發生更新的時候,我們可以用下面的代碼實現:

Task.bind('created', function(ev, task){  var el = $('<li>').html(todo.name);  el.appendTo($('#todos'));  task.bind('updated', function(){    el.html(this.name)  }).bind('destroyed', function(){    el.remove()  })})

 

javascriptMVC教程目錄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.