backbone.jsのCollectionのフィルタリング

backbone.jsに慣れようと思って、ちょこちょこサンプルを書いているわけですが、
今日はインクリメンタルサーチっぽいUIを書いて見ました。

サンプルはこちら
http://dl.dropbox.com/u/494487/backbone-sample/filtering/index.html

データは静的にもってるだけで、サーバーからfetchとかはしてません。

ModelとCollection

    // Model
    var Track = Backbone.Model.extend({});

    // Collection
    var TrackList = Backbone.Collection.extend({
	model: Track,

	search : function(query){
	    if(query == "") return this;

	    var pattern = new RegExp(query, "gi");
	    return _(this.filter(function(data) {
		return pattern.test(data.get("title"));
	    }));
	}	
    });

Collectionに絞込み用のsearch関数を持たせてます。
戻り値を_()で囲むことによって、絞り込み結果に対して、view側でunderscore.jsのメソッドが使えます

View

    // main view
    var AppView = Backbone.View.extend({

	events: {
	    "keyup #new-input": "search"
	},

	initialize: function() { 
	    this.template = _.template($("#list-container").html());

	    this.collection.bind("all", this.render, this);
	    this.collection.bind("reset", this.addAll, this);
	    // this.collection.fetch()
	},

	render: function(data) {
	    $(this.el).html(this.template);
	    return this;
	},

	renderList: function(entires){
	    $("#trackList").html("");
	    entires.each(this.addOne);
	    return this;
	},

	search: function(){
	    var query = $('#new-input').val();
	    this.renderList(this.collection.search(query));
	},

	addOne: function(track){
	    var view = new TrackView({model: track});
	    $("#trackList").append(view.render().el);
	},

	addAll: function(){
	    this.collection.each(this.addOne);
	}
	
    });

eventsに{"keyup #new-input": "search"}を設定して、入力があった場合にviewのsearch関数が呼ばれるようになります。

viewのsearch関数は保持しているcollectionのsearchメソッドを呼び、絞り込んだ結果をrenderList関数に渡します。


全体では以下のような感じです。

$(function(){

    // Model
    var Track = Backbone.Model.extend({});

    library = [new Track({title: "I Saw Her Standing There"}),
	       new Track({title: "Misery"}),
	       new Track({title: "Anna (Go To Him)"}),
	       new Track({title: "Chains"}),
	       new Track({title: "Boys"}),
	       new Track({title: "Ask Me Why"}),
	       new Track({title: "Please Please Me"}),
	       new Track({title: "Love Me Do"}),
	       new Track({title: "P.S. I Love You"}),
	       new Track({title: "Baby It's You"}),
	       new Track({title: "Do You Want To Know A Secret"}),
	       new Track({title: "A Taste Of Honey"}),
	       new Track({title: "There's A Place"}),
	       new Track({title: "Twist And Shout"})];
    
			  
    // Collection
    var TrackList = Backbone.Collection.extend({
	model: Track,

	search : function(query){
	    if(query == "") return this;

	    var pattern = new RegExp(query, "gi");
	    return _(this.filter(function(data) {
		return pattern.test(data.get("title"));
	    }));
	}	
    });

    // Collection instance
    var tracks = new TrackList(library);

    // each track view
    var TrackView = Backbone.View.extend({

	initialize : function(){
	    this.template = _.template($("#track-template").html());
	},

	render: function(data) {
	    $(this.el).html(this.template(this.model.toJSON()));
	    return this;
	}
    });
    
    // main view
    var AppView = Backbone.View.extend({

	events: {
	    "keyup #new-input": "search"
	},

	initialize: function() { 
	    this.template = _.template($("#list-container").html());

	    this.collection.bind("all", this.render, this);
	    this.collection.bind("reset", this.addAll, this);
	    // this.collection.fetch()
	},

	render: function(data) {
	    $(this.el).html(this.template);
	    return this;
	},

	renderList: function(entires){
	    $("#trackList").html("");
	    entires.each(this.addOne);
	    return this;
	},

	search: function(){
	    var query = $('#new-input').val();
	    this.renderList(this.collection.search(query));
	},

	addOne: function(track){
	    var view = new TrackView({model: track});
	    $("#trackList").append(view.render().el);
	},

	addAll: function(){
	    this.collection.each(this.addOne);
	}
	
    });


    window.App = new AppView({collection: tracks});
    $("#content-container").append(window.App.render().el);
    window.App.addAll();
    
});
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>backbone sample - filter</title>
  </head>

  <body>
    <h1>BackBone Filter Sample</h1>

    <div id="content-container">
    </div>

    <script id="list-container" type="text/template">
      <div>
	<div>
	  <input type="text" id="new-input" placeholder="Type something">
	  <div class="block">
	    <ul id="trackList" class="list">
	    </ul>
	  </div>
	</div>
      </div>
    </script>

    <script id="track-template" type="text/template">
      <li>
	<h4><%= title %></h4>
      </li>
    </script>

    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.2/underscore-min.js" type="text/javascript" charset="utf-8"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js" type="text/javascript" charset="utf-8"></script>
    <script src="app.js" type="text/javascript"></script>

  </body>

</html>