大きな独り言

はてなダイアリーにも書いてましたが続かないので、再チャレンジではてなブログ始めてみました

"ステートフルJavaScript"に書いてあるbackboneのToDoリストアプリケーションの作成を試してみる

  • 社内で開催されていたbackbone読書会で挫折してそろそろ1年、jsを勉強したい機運が高まってきたので写経してみた
  • いまさら感は高いが、ステートフルJavaScriptを読んでも内容が全然頭に入ってこないので、ソース書いて熟読したうえで読み直そうと決意
  • 必要なライブラリやbackbone本体は自前で用意しようと思ったが、ダルいので公開されているサンプルを使う
mkdir backbone
git clone git@github.com:maccman/book-assets.git
cp -r book-assets/ch12/todos/ backbone/
cd backbone
rm todos.js
  • todos.jsだけは写経するので削除
  • 以下を写経して、todos.jsとして保存。(本に載ってたコメントは書かないで、自分のメモがコメントされています)
jQuery(function($){

  window.Todo = Backbone.Model.extend({
    defaults: {
      done: false
    },

    toggle: function(){
      this.save({done: !this.get("done")});
    }
  });

  window.TodoList = Backbone.Collection.extend({
    model: Todo,
    // これでローカルストレージから読み込まれる?
    localStorage: new Store("todos"),
    done: function(){
      return this.filter(
        function(todo){ 
          return todo.get('done'); 
        }
      );
    },
    remaining: function(){
      return this.without.apply(this, this.done());
    }
  });

  window.Todos = new TodoList;

  window.TodoView = Backbone.View.extend({
    tagName: "li",
    template: $("#item-template").template(),
    events: {
      "change   .check"        : "toggleDone",
      "dblclick .todo-content" : "edit",
      "click    .todo-destroy" : "destroy",
      "keypress .todo-input"   : "updateOnEnter",
      "blur     .todo-input"   : "close",
    },
    initialize: function(){
      // bindしておかないと各functionでのコンテキスト(this)が指すものがイベントになってしまう。
      _.bindAll(this, 'render', 'close', 'remove');
      this.model.bind('change',  this.render);
      this.model.bind('destroy', this.remove);
    },

    render: function(){
      var element = jQuery.tmpl(this.template, this.model.toJSON());
      $(this.el).html(element);
      return this;
    },

    toggleDone: function(){
      // ここのthisはなんでイベントにならないの?
      this.model.toggle();
    },

    close: function(e){
      this.model.save({content: this.input.val()});
      $(this.el).removeClass("editing");
    },

    updateOnEnter: function(e){
      if (e.keyCode == 13) e.target.blur();
    },

    remove: function(){
      $(this.el).remove();
    },

    destroy: function(){
      this.model.destroy();
    }
   
  });

  window.AppView = Backbone.View.extend({
    el: $("#todoapp"),
    events: {
      "keypress #new-todo"     : "createOnEnter",
      "click    .todo-clear a" : "clearCompleted"
    },

    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render');
      this.input = this.$("#new-todo");
      Todos.bind('add'     ,this.addOne);
      Todos.bind('refresh' ,this.addAll);
      Todos.fetch();
    },

    addOne: function(todo) {
      var view = new TodoView({model:todo});
      this.$("#todo-list").append(view.render().el);
    },

    addAll: function(){
      // eachはcollectionのfunctionぽい
      Todos.each(this.addOne);
    },

    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      var value = this.input.val();
      if (!value) return;
      Todos.create({content: value});
      this.input.val('');
    },

    clearCompleted: function() {
      _.each(Todos.done(), function(todo){ todo.destroy(); });
      return false;
    }
  });

  window.App = new AppView;

})
  • 上記でとりあえず動いた。かなりの複雑さで、現段階ではとても理解できる気がしないけど、根気強くソース読んで脳内にインストールしていけば何も見なくても実装できるようになるはず(なってもらわないと困る)
  • よくみたらサンプルと本に載ってるコード違った。本だと簡略化されてるぽい。(clearCompletedの関数使ってるとこないよなーと思ったし)
  • 次の機会ではサンプルコード見ずにサンプルコードに載っている編集機能やチェック済みTodoを消す処理を書いてみたい