Part 7: 編輯任務列表

z.w發表於2015-01-16

準備

開始本教程之前,你需要了解以下幾點:

  • alexyoung / dailyjs-backbone-tutorial提交到了465523f版本
  • 第二部分中的API key
  • 第二部分中的“Client ID”
  • 更新app/js/config.js成你自己的key(如果你檢出了我的程式碼)

要檢出原始碼,請執行以下命令(或用Git GUI工具):

git clone git@github.com:alexyoung/dailyjs-backbone-tutorial.git
cd dailyjs-backbone-tutorial
git reset --hard 465523f

當前任務列表

在前幾個部分,我們用Backbone.sync實現了creat方法和怎麼新建一個任務到檢視模板中,如你還記得我們通過繼承AddListView新建編輯任務列表的檢視,因為新建和編輯差不多

編輯任務列表之前我們需要一個方法把選中和狀態儲存起來,這樣即使我們從伺服器端拿任務列表,我也會有一個預設被選中的任務列表。

我們新建一個新的models物件通過檢查集合和檢視來實現這個,其中可以有個activeList.

app/js/app.js中新增一個models屬性,任務列表載入後設定activemodel:

App.prototype = {
  views: {},
  collections: {},
  models: {},
  connectGapi: function() {
    var self = this;
    this.apiManager = new ApiManager(this);
    this.apiManager.on('ready', function() {
      self.collections.lists.fetch({ data: { userId: '@me' }, success: function(res) {
        self.models.activeList = self.collections.lists.first();
        self.views.listMenu.render();
      }});
    });
  }
};

app/js/views/lists/menu.js保證activeModel當工作列導航渲染的時候被使用:

renderMenuItem: function(model) {
  var item = new ListMenuItemView({ model: model });
  this.$el.append(item.render().el);

  if (model.get('id') === bTask.models.activeList.get('id')) {
    item.open();
  }
},

如果模型和當前這個想相同則open這個檢視。修改app/js/views/lists/menuitem.js確保ListMenuItemView能夠檢查到activeModel:

open: function() {
  bTask.models.activeList = this.model;
  return false;
}

現在我們就實現了展開我們被選中的任務列表了,這樣新建的任務就可以很容易加到你當前選中的列表中

編輯任務列表表單

當我們點選“編輯”連結時,需要給表單填充需要修改的任務資料,這樣我們才能修改,這個有點像addList方法 開啟app/js/views/app.js, 新增依賴EditListView模組:

define([
  'text!templates/app.html'
, 'views/lists/add'
, 'views/lists/edit'
],

function(template, AddListView, EditListView) {

#edit-list-button按鈕新增事件:

events: {
  'click #add-list-button': 'addList'
, 'click #edit-list-button': 'editList'
},

最好activeList基礎是上,新增EditListView表單的一個例項化方法editList:

editList: function() {
  var form = new EditListView({ model: bTask.models.activeList });

  this.$el.find('#list-editor').html(form.render().el);
  form.$el.find('input:first').focus();

  return false;
}

這個方法和addList有點類似,他可以用於不同的模型:

listForm: function(form) {
  this.$el.find('#list-editor').html(form.render().el);
  form.$el.find('input:first').focus();

  return false;
},

addList: function() {
  return this.listForm(new AddListView({ model: new bTask.collections.lists.model({ title: '' }) }));
},

editList: function() {
  return this.listForm(new EditListView({ model: bTask.models.activeList }));
}

DRY!(設計模式中不用重複自己)

儲存修改

和(app/js/gapi.js)新建差不多,任務被修改需要通過Backbone.sync更新:

// Around line 97, after 'create'
case 'update':
  requestContent['resource'] = model.toJSON();
  request = gapi.client.tasks[model.url].update(requestContent);
  Backbone.gapiRequest(request, method, model, options);
break;

通過用Google’s API的tasklist來實現update方法,詳細的文件在這裡(asklists/update

相對於用Backbone模型把這段邏輯放到Backbone.sync比較好些,因為和google相關的都在這個地方.

可以用switch根據不同模型的url實現獲取模型ID引數並插入。

Backbone.sync = function(method, model, options) {
  var requestContent = {};
  options || (options = {});

  switch (model.url) {
    case 'tasks':
      requestContent.task = model.get('id');
    break;

    case 'tasklists':
      requestContent.tasklist = model.get('id');
    break;
  }

現在任務列表就可編輯了,但有一件事沒有做—給選中的列表新增“active”狀態。

當前選中的任務列表

開啟app/js/views/lists/menuitem.js修改open方法,當你選中導航列表時給當前選中的新增一個類名:

open: function() {
  if (bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem.$el.removeClass('active');
  }

  bTask.models.activeList = this.model;
  bTask.views.activeListMenuItem = this;
  this.$el.addClass('active');

  return false;
}

bTask.views.activeListMenuItem用於儲存被開啟的當前的檢視。需要注意下這裡我們用的是this.$el。儘量的少用jQuery更多得用Backbone來替代,因為$()是jQuery的用於查詢元素用。

要我為什麼用bTask.views.activeListMenuItem儲存引用而不是$('.list-menu-item').removeClass('active'),我也很難講—畢竟jQuery在也有一定的用處

這個就引出另一個問題:我們到底用什麼去儲存對一個模型的引用了?我們現在用的是ListMenuItemView,大多數情況下Backbone更專注於模型的UI介面相對於它的內部狀態。我們試試刪除對bTask.models的引用。

開啟app/js/app.js刪除設定activeList那行和models物件。然後修改app/js/views/lists/menuitem.jsopen方法:

open: function() {
  if (bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem.$el.removeClass('active');
  }

  bTask.views.activeListMenuItem = this;
  this.$el.addClass('active');

  return false;
}

接下來開啟app/js/views/app.js修改AppView類,確定editList使用的是bTask.views.activeListMenuItem.model,最後,在app/js/views/lists/menu.js啟用預設項:

renderMenuItem: function(model) {
  var item = new ListMenuItemView({ model: model });
  this.$el.append(item.render().el);

  if (!bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem = item;
  }

  if (model.get('id') === bTask.views.activeListMenuItem.model.get('id')) {
    item.open();
  }
},

我覺得在Backbone中,應該不儲存應用程式內部狀態,而是通過檢視上的類名來實現,這樣可以簡化應用程式內部的邏輯。

為了使介面上看起來更明顯,我們在app/css/app.css新增li.active { font-weight: bold }樣式

總結

這個部分我們基於Part 6實現了對任務列表的編輯,我們還得跟蹤當前修改具體是那個任務列表。

你還知道了為什麼用this.$el$()來儲存元素的引用的原因,同時我們還了解到:在檢視中通過類來儲存它的狀態,避免與外部的互動的好處。

教程中完整的程式碼可以在這裡找到:alexyoung / dailyjs-backbone-tutorial, commit 0953c5d

相關文章