Backbone.jsはじめてみました6

写経。http://d.hatena.ne.jp/griefworker/20111024/backbone

by FiNGAHOLiC

HTML

<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
<h1>Backbone Todo</h1>
<div id="main">
    <div id="task-list"></div>
    <input id="name-input" type="text">
    <input id="add-input" type="button" value="add">
</div>
<script id="task-template" type="text/template">
    <% if(isEditing){ %>
        <input class="name-input" type="text" value="<%= name %>">
        <input class="save-input" type="button" value="save">
        <input class="cancel-input" type="button" value="cancel">
        <input class="delete-input" type="button" value="delete">
    <% }else{ %>
        <% if(completed){ %>
            <input type="checkbox" checked>
            <del><%= name %></del>
        <% }else{ %>
            <input type="checkbox">
            <%= name %>
        <% } %>
        <a class="edit-link" href="javascript:void(0);">[edit]</a>
    <% } %>
</script>

CSS

h1{
    font-size:200%;
    font-weight:bold;
    line-height:2;
}

JavaScript

(function($, window, document, undefined){
    
    // タスクを表すクラス
    // タスクのデータを格納するモデルなので、backbone.Modelを継承。
    var Task = Backbone.Model.extend({
        // 初期値を指定。
        // new でオブジェクトを生成した時にまずこの値がattributesに格納される。
        defaults : {
            name : '',
            completed : false,
            isEditing : false
        },
        // destroyをオーバーライド。
        // 本来ならサーバーと通信するが、サンプルではデータを永続化させないため
        // destroyイベントのみ発生させる。
        destroy : function(){
            this.trigger('destroy', this);
        },
        // setメソッドに渡されたデータを検証する。
        // 何か値を返すと検証エラー扱いになるので、
        // 不正な値だったときはエラーメッセージを返すといい。
        validate : function(attrs){
            // underscore.jsのメソッドを利用
            if(_.isString(attrs.name) && _.isEmpty(attrs.name)){
                return 'task name is empty!';
            };
        }
    });
    
    // タスクリストを表すクラス
    // タスクのコレクションを扱うのでBackbone.Collectionを継承。
    var TaskList = Backbone.Collection.extend({
        // コレクションが扱うモデルの型。
        // 指定しなくてもよいが、指定しておくとaddにTask以外のオブジェクトが
        // 渡された時に指定したモデルでラップしてくれる。
        model : Task,
        // 初期化
        initialize : function(){
            // タスクリストにタスクが追加された時に発生するaddイベントに
            // ハンドラを登録する。
            this.bind('add', this._onAdd, this);
        },
        _onAdd : function(task){
            task.bind('destroy', this._onDestroy, this);
        },
        // タスクがdestroyイベントを発生させたらタスクリストから削除する。
        _onDestroy : function(task){
            this.remove(task);
        }
    });
    
    // タスクを表示するビュー。
    // ビューはbackbone.Viewを継承。
    var TaskView = Backbone.View.extend({
        // ビューをレンダリングするタグの名前を指定。
        tagName : 'div',
        // テンプレート。
        // underscoreのテンプレートを使用。
        template : _.template($('#task-template').html()),
        // イベントハンドラのマッピング。
        // CSSセレクタにマッチした要素にイベントハンドラを自動でセットしてくれる。
        events : {
            'change input[type="checkbox"]' : '_onCheck',
            'click a.edit-link' : '_onEdit',
            'click...