准备

开始本教程之前,你需要了解以下几点:

  • 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