JavaScriptを超シンプルなMVCで書く(2)
以前の記事「JavaScriptを超シンプルなMVCで書く」を見返すとイマイチなので書き直し。
lonely-programmer.hatenablog.jp
以下挙動のアプリケーションを作ります。
・ボタンをクリックする毎に、白石麻衣の所属するグループが、乃木坂46 / 欅坂46 / SKE46 の順番でループする。
まずモデル。
var model = (function() { /* privateメンバ */ var count = 0; var groups = []; // 現在のグループを取得 var getNowGroup = function() { return groups[getIndex()]; }; // 現在のインデックスを取得 var getIndex = function() { // 0 → 1 → 2 → 0 → 1 → 2... とインデックスをループ return count % groups.length }; /* publicメンバ */ // グループを追加 var addGroup = function(group) { groups.push(group); }; // グループを変更 var changeHerGroup = function() { count++; $(this).trigger('groupIsChanged'); }; // グループ名を表示 var getGroupName = function () { return getNowGroup().getName(); }; // publicメンバを開示 return { addGroup: addGroup, changeHerGroup: changeHerGroup, getGroupName: getGroupName }; })();
モジュールパターンでオブジェクトを返却してます。
このオブジェクトはクロージャです。
privateメンバには外部からアクセスできません。
注目すべきはここ。
var changeHerGroup = function() { count++; $(this).trigger('groupIsChanged'); };
モデル内の値に変化があった場合、'groupIsChanged'イベントが発火されます。
オブジェクトではなく、コンストラクタ関数で返却する場合は、メソッド後半を以下のように書き変えます。
// コンストラクタ関数にpublicメンバを登録 var Constr = function() { this.addGroup = addGroup; this.changeHerGroup = changeHerGroup; }; // プロトタイプにpublicメンバを登録 Constr.prototype = { getGroupName: getGroupName }; // コンストラクタ関数を返却 return Constr;
グループ用のオブジェクトをモデルのプロパティに追加。
変数nameはprivateです。
// グループ用のコンストラクタ関数 function Group(name) { var name = name; // 書かなくても良い this.getName = function() { return name; }; } // グループ追加 model.addGroup(new Group('乃木坂46')); model.addGroup(new Group('欅坂46')); model.addGroup(new Group('SKE48'));
ボタンがクリックされると、モデル内の値を更新。
// ボタンクリック時のイベントハンドラ $('button').on('click', function() { model.changeHerGroup(); });
カスタムイベントを作成。
// レンダリング用カスタムイベント $('span').on('render', function() { $(this).text(model.getGroupName()); });
モデル内の値に変化があれば、再レンダリングを行います。
jQueryのon()メソッドは、DOMオブジェクト以外も監視できることを最近知りました…。
// モデルを監視 $(model).on('groupIsChanged', function() { // テキストを再レンダリング $('span').trigger('render'); });
こちら、初期表示。
// テキストを初期表示 $('span').trigger('render'); // JavaScript処理完了後に画面表示 $('body').css('display', 'block');
ES6のことは一切考慮してません。笑
次回以降の記事で、ES6に書き換えてみようと思います。
以下、全コード。
<html> <script src="jquery.js"></script> <script> $(function() { var model = (function() { /* privateメンバ */ var count = 0; var groups = []; // 現在のグループを取得 var getNowGroup = function() { return groups[getIndex()]; }; // 現在のインデックスを取得 var getIndex = function() { // 0 → 1 → 2 → 0 → 1 → 2... とインデックスをループ return count % groups.length }; /* publicメンバ */ // グループを追加 var addGroup = function(group) { groups.push(group); }; // グループを変更 var changeHerGroup = function() { count++; $(this).trigger('groupIsChanged'); }; // グループ名を表示 var getGroupName = function () { return getNowGroup().getName(); }; // publicメンバを開示 return { addGroup: addGroup, changeHerGroup: changeHerGroup, getGroupName: getGroupName }; })(); // Controller (function() { // グループ用のコンストラクタ関数 function Group(name) { var name = name; // 書かなくても良い this.getName = function() { return name; }; } // グループ追加 model.addGroup(new Group('乃木坂46')); model.addGroup(new Group('欅坂46')); model.addGroup(new Group('SKE48')); // ボタンクリック時のイベントハンドラ $('button').on('click', function() { model.changeHerGroup(); }); })(); // View (function() { // モデルを監視 $(model).on('groupIsChanged', function() { // テキストを再レンダリング $('span').trigger('render'); }); // レンダリング用カスタムイベント $('span').on('render', function() { $(this).text(model.getGroupName()); }); // テキストを初期表示 $('span').trigger('render'); // JavaScript処理完了後に画面表示 $('body').css('display', 'block'); })(); }); </script> <body style="display: none"> <button>グループ変更</button> <p><span></span>の白石麻衣です。</p> </body> </html>