Backbone.jsでCollectionを触ってみる
Collectionを触ったことが無かったので、簡単に触っておこうと思います。
Cllectionは複数のModelを突っ込める器のようなものです。
DBに例えると、Modelがレコードだとすると、Collectionはテーブルになります。
以下、コードの全容。
タスクリストのようなアプリケーションです。
フォームにテキストを入力してボタンを押すと、リストにテキストが追加されます。
ただし、ブラウザの更新ボタンを押すとすべて初期化されます。笑
<html> <script src="underscore-min.js"></script> <script src="jquery.js"></script> <script src="backbone-min.js"></script> <script> $(function() { // モデル var Model = Backbone.Model.extend(); // コレクション var Collection = Backbone.Collection.extend({ model: Model, }); var collection = new Collection; // ビュー var View = Backbone.View.extend({ el: 'body', events: { 'click button': 'addItem', }, initialize: function() { this.listenTo(this.collection, 'add', this.render); }, addItem: function() { this.collection.add({ name: $('input').val() }); }, render: function(model) { $('ul').append($('<li>').text(model.get('name'))); $('input').val(''); }, }); var view = new View({ collection: collection }); }); </script> <body> <input type="text"> <button>追加</button> <ul> </ul> </body> </html>
var Collection = Backbone.Collection.extend({ model: Model, }); var collection = new Collection;
コレクションの作成。使用するモデルを指定します。
var View = Backbone.View.extend({ el: 'body', events: { 'click button': 'addItem', }, initialize: function() { this.listenTo(this.collection, 'add', this.render); }, addItem: function() { this.collection.add({ name: $('input').val() }); }, render: function(model) { $('ul').append($('<li>').text(model.get('name'))); $('input').val(''); }, }); var view = new View({ collection: collection });
ビューの作成。
コレクションにモデルが追加されると、コレクションで「add」イベントが発火されます。
ビューはaddイベントの発火を感知し、renderメソッドが実行されます。
render: function(model) { $('ul').append($('<li>').text(model.get('name'))); $('input').val(''); },
このメソッドですが、自動で引数に追加されたモデルが渡されるようです。こりゃ便利だ。
Backbone.jsいいなぁ。でも古いフレームワークなので、新しいブログ記事が全く出てこない。
JavaScriptフレームワークの歴史を追うってことで、今後はReact.jsとかも触っていきたいです。
Backbone.jsで超シンプルなMVCを書く
前回はピュアなJavaScriptでシンプルなMVCを書くという趣旨でしたが…
「フレームワークを使った方がシンプルに書けるのでは?」
という考えが巡り始めたので、今回は前回の内容をBackbone.jsで書き換えます。
Backbone.jsを簡単に説明すると…
日本語に訳して「背骨」なだけあって、MVCの枠組みだけを提供してくれます。
jQueryと共存できる…というか、Backbone.jsのライブラリ自体がjQueryに依存してます。
要は、大枠はBackbone.jsがやるから、細かいことはjQueryでやってくれってことです。
■ Model
var Model = Backbone.Model.extend({ buttonStatus: true, changeData: function() { if (this.buttonStatus) { this.buttonStatus = false; } else { this.buttonStatus = true; } this.trigger('changingIsDone'); } }); var model = new Model;
まずモデル。データは全部モデルに持たせます。
モデルからDOM操作は行いません。
this.trigger('changingIsDone');
データに変更があれば、モデルの「changingIsDone」イベントが発火されます。
※HTMLの各要素から発火されるイベント(onclick, onchange等)とは別の概念と思われます。
■ View
var View = Backbone.View.extend({ el: 'body', events: { 'click button': 'switchButtonName', }, initialize: function() { this.listenTo(this.model, 'changingIsDone', this.render); this.render(); }, switchButtonName: function() { this.model.changeData(); }, render: function() { var buttonName; if (this.model.buttonStatus) { buttonName = '乃木坂46'; } else { buttonName = '欅坂46'; } $('button').text(buttonName); }, }); var view = new View({ model: model });
events: { 'click button': 'switchButtonName', },
イベントハンドラを登録してます。
switchButtonNameメソッドから、モデルを操作してます。
initialize: function() { this.listenTo(this.model, 'chngingIsDone', this.render); this.render(); },
initializeメソッドはPHPの__construct()のようなものです。
listenToメソッドで、モデルの「chngingIsDone」イベントの監視を開始しました。
「chngingIsDone」イベントが発火すると、自動でrenderメソッドが実行されます。
render: function() { var buttonName; if (this.model.buttonStatus) { buttonName = '乃木坂46'; } else { buttonName = '欅坂46'; } $('button').text(buttonName); },
renderメソッドではDOM操作を行っています。
ここで面白いのは、モデルの「changingIsDone」イベントを監視できるViewを複数追加できることです。
■ View 2
var SubView = Backbone.View.extend({ el: 'p', initialize: function() { this.listenTo(this.model, 'changingIsDone', this.render); this.render(); }, render: function() { var description; if (this.model.buttonStatus) { description = '2011年に結成されました。'; } else { description = '2015年に結成されました。'; } $(this.el).text(description); }, }); var subView = new SubView({ model: model });
こちらのViewもモデルを監視しています。
つまり、モデルに変化があれば、View, SubViewの両方がレンダリングされる…ということです。
<body> <button></button> <p></p> </body>
こちら、HTML。
ピュアなJavaScriptより、こちらの方がシンプルに書けました。
デメリットは、そのフレームワークの経験者でないと、システムの修正を気軽にできないことでしょうか…。
JavaScriptを超シンプルなMVCで書く
- 単一のWebページに、簡単なイベントハンドラを3つ、4つ程書きたい。
- でもJavaScriptフレームワークを使う程の規模ではない。
- 本能の赴くままにイベントハンドラを書いてたら、訳が分からなくなってきた。
- 触るとバグりそうなので、もう触りたくない。
上記のようなこと、あると思います。というか、今の自分です。
サラッと見通しよく書きたいなぁ…ということで、簡単なサンプルを書いてみました。
var model = { flg: true, // 初期値 change: function() { if(this.flg) { this.flg = false; } else { this.flg = true; } } }
まずモデルは、データ層として考える。プロパティ = アプリが保持すべきデータ。
条件分岐はモデルでやる。
例えば、ファミコンのボタンが押されたら、その「ボタンを押した」という信号がモデルに渡されるイメージ。
信号をどう捌くかは、モデルに任せる。
自分が昔よくやってたのが、
var oldText = $('button').text(); var newText; if (oldText === '乃木坂46') { newText = '欅坂46'; } else { newText = '乃木坂46'; } $('button').text(newText);
DOMの値を直接読みにいって、それを使って諸々の処理を行う。
もうやめとこう。これからは、モデルのプロパティに状況を持たせる。DOMが持ってるデータは使わない。
var view = { render: function() { if(model.flg) { $('button').text('乃木坂46'); } else { $('button').text('欅坂46'); } } }
ビューからモデル内のデータを読んで、レンダリングする。
$(function() { $('button').on('click', function() { model.change(); view.render(); }); view.render(); // 初期表示 });
コントローラ。モデルとビューを使う。
まとめると、
(function() { var model = { flg: true, change: function() { if(this.flg) { this.flg = false; } else { this.flg = true; } } } var view = { render: function() { if(model.flg) { $('button').text('乃木坂46'); } else { $('button').text('欅坂46'); } } } $(function() { $('button').on('click', function() { model.change(); view.render(); }); view.render(); }); })();
無名関数で囲って、グローバル変数は作らないようにする。
これで何とか見通しよくなってくれないかなぁ…。