Backbone.jsの基本

細かいことはサンプルコードで、ということで……

backbone.js v1.0.0

function l(s) {console.log(s)};
function a(s) {alert(s)};
l('*****start*****');

l('*****Backbone.Model*****');
var Todo = Backbone.Model.extend({
    defaults: {
        title: ''
        ,completed: false
    }
    ,initialize: function() {
        l('*** model initialize***');
        this.on('change', function(){l('model has changed');});
        this.on('change:title', function(){l('title has changed');});
        
        this.on('invalid', function(){l(this.validationError)});
    }
    ,validate: function(attrs) {
        if(!_.isBoolean(attrs.completed)) {
            return 'completedはブーリャンでお願いしますね!';
        }
    }
});

var todo = new Todo({title: 'sample'});
l(JSON.stringify(todo));
l(todo.get('title'));
l(todo.attributes.title);
l(todo.hasChanged());//false
l(todo.hasChanged('title'));//false
l(todo.hasChanged('completed'));//false

todo.set('title', 'changed title');
l(todo.get('title'));
l(todo.hasChanged());//true
l(todo.hasChanged('title'));//true
l(todo.hasChanged('completed'));//false

todo.set('completed', 'hoge');
todo.save();// invalid


l('*****Backbone.View*****');
var TodoView = Backbone.View.extend({
    id: 'sample_view'
    ,className: 'sample_class1 sample_class2'
    ,tagName: 'li'
    ,template: _.template('example')
    ,events: {
        'dblclick label': 'edit'
        ,'keypress .edit': 'updateOnEnter'
        ,'blur .edit': 'close'
    }
    ,render: function() {
        this.$el.html(this.template(this.model.toJSON()));
        this.input = this.$('.edit');
        return this;
    }
    ,edit: function(){}
    ,updateOnEnter: function(){}
    ,close: function(){}
});

var todoView = new TodoView();
l(todoView.el);
l(todoView.$el);

var button = $('<button></button>');
todoView.el = button;
l(todoView.el);//jQueryオブジェクトになっちゃってる
l(todoView.$el);//前から変わってない

todoView.setElement(button);
l(todoView.el);//期待通り
l(todoView.$el);//期待通り

todoView.el = '<input/>';
l(todoView.el);//期待通り
l(todoView.$el);//前から変わってない

l('*****Backbone.Collection*****');
(function(){
    var collection = new Backbone.Collection;
    collection.add([{id:1, name:'dog'},{id:2, name:'cat'}]);
    l(JSON.stringify(collection));
    
    collection.add([{id:1, name:'dogdog'}], {merge:true});
    l(JSON.stringify(collection));//id:1のモデルのnameがマージされてdogdogになる
    l(collection.get(1));//上でマージしたやつ
    l(collection.get(1).idAttribute);//id
})();

var TodoList = Backbone.Collection.extend({
    model:Todo
});
var todoList = new TodoList();
todoList.on('add', function(model){l(model.cid + ' added to collection');});
todoList.on('remove', function(model){l(model.cid + ' removed from collection');});
todoList.on('change', function(model){l(model.cid + ' changed in collection');});
todoList.on('change:title', function(model){l(model.cid + ' title changed in collection');});
todoList.on('reset', function(models,options){
    l('collection reset');
    l(models);
    _.each(options.previousModels, function(m){
        l('  ' + m.cid + ' removed');
    });
    _.each(models.models, function(m){//models.modelsってなんだ……
        l('  ' + m.cid + ' added');
    });
});

var todo = new Todo({title: 'sample'});
l(todo.id);//undefined
l(todo.cid);//c1とか
l(todo.idAttribute);//id
todoList.add([todo]);
l(JSON.stringify(todoList));
//l(list.get(0).get('title'));//idがないからidでgetはできない
l(todoList.get(todo.cid).get('title'));//sample

todoList.add([{id: 0, title: 'sample'}]); //別にnew Todo()しなくても属性値だけでaddできちゃう
l(todoList.get(0).get('completed'));//Todoとしてaddされているのでちゃんとfalseと出力される

//一旦Collectionにaddしたならmodel単体で操作してもCollection側のイベントハンドラは有効
var temp = todoList.get(0);
temp.set('title', 'タイトル変えてみるわ');//Todo,TodoList両方でchangeイベントがハンドリングされる
todoList.remove(0);

todoList.reset([{id: 0, title: 'dog'}, {title: 'cat'}]);//完全に中身を作り直す
todoList.set([{id:0, title: 'dogdog'}, {title: 'mouse'}]);//add,remove,changeを駆使する
todoList.reset();//resetイベントのみ発生

Backbone.Model

RubyActiveRecordみたいにhasChangedで変更有無を調べられるのはよかった。setter(model.set(attr, val))でセットしないと変更されたと見なされないのも同じ。

バリデーションはイベントだけが提供されていて、Railsのsexy validationみたいなのはない。

Backbone.View

HTML要素1つに紐付くっぽい。そのdom elementを一から作るなら、

var v = Backbone.View.extend({
    id: 'sample_view'
    ,className: 'sample_class1 sample_class2'
    ,tagName: 'li'
});

と言った調子で表現できる。

既に存在する要素に対しては

var v = Backbone.View.extend({
    el: '#sample_view'
});
var v = Backbone.View.extend({});
v.setElement($('<button></button>'));

といった調子。newしたあとにv.elに直接代入するのはよくないっぽいので上記2つ以外はやらない方がたぶんいい。

setElement()で要素を設定したらel$el両方が自動セットされる。