前回は簡単にMilkcocoaでチャットアプリケーションを作りました。今回はそれを拡張する形で、
SPAによるフロントエンド設計の重要性
MilkcocoaなどによりBaaSのWebアプリケーションを作る際は、
SPA
そこで設計が非常に大切になります。SPAはすなわち、
MVCとは?
MVCについて説明する前に、
MVCとはGUIアプリケーションを、

チャットアプリケーションをMVC設計にする
チャットアプリケーションの拡張
チャットアプリケーションを拡張して行きます。以下の機能を追加します。
- メッセージ削除機能
まずこれについて前回と同様にレイアウトを構成してnobackendで実装しておき、
レイアウトを構築する
まず簡単にレイアウトを構築しましょう。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="chat-board">
<div id="chat-content-from">
<input id="chat-room-text">
<button>メッセージ入力</button>
</div>
<div>
<p>こんにちは!</p>
<button>削除</button>
</div>
<div>
<p>どうもはじめまして!</p>
<button>削除</button>
</div>
<div>
<p>やぁやぁ</p>
<button>削除</button>
</div>
<div>
<p>どもども</p>
<button>削除</button>
</div>
</div>
</body>
</html>
本来であればBEMやSMACSと言ったCSS等の設計も必要なのですが、
layout.

モデルを書く
画面のレイアウトができたところで、
(function(global){
// プロトタイプ継承用のモデル
// addViewで、通知するViewを設定
// notifyにより、Viewに通知
var Model = function(){};
Model.prototype.addView = function(view){
this.view = view;
}
Model.prototype.notify = function(){
if( this.view != null ){
this.view.update();
}
}
var ChatElement = function(text){
this.text = text;
this.id = Date.now();
}
ChatElement.prototype = new Model();
ChatElement.prototype.getText = function(){
return this.text;
}
var Chat = function(){
this.elements = [];
this.id = Date.now();
}
Chat.prototype = new Model();
Chat.prototype.getName = function(){ return this.name; }
Chat.prototype.getElements = function(){ return this.elements; }
Chat.prototype.add = function(chatElement){
this.elements.push(chatElement);
this.notify();
}
Chat.prototype.remove = function(id){
var changed = false;
for(var i in this.elements){
if( this.elements[i].id === id ){
this.elements.splice(i,1);
changed = true;
}
}
if( changed ){
this.notify();
}
}
global.Chat = Chat;
global.ChatElement = ChatElement;
}(window));
重要な点はModelが変更されたときに、
Modelが書けたらテストコードを作成しましょう。
// Modelテスト用のViewモック
var ViewMock = function(){
this.call_update_counter = 0;
}
ViewMock.prototype.update = function(){
this.call_update_counter++;
}
ViewMock.prototype.getCounter = function(){
return this.call_update_counter;
}
// 簡易assert関数。
function assertEqual(msg,expect,value){
if( expect === value ){
showMessage(msg + " is ok! ");
}else{
showMessage(msg + " is failed! Expect is " + expect + " but value is " + value );
}
}
function showMessage(text){
var p = document.createElement("p");
p.appendChild(document.createTextNode(text));
document.body.appendChild(p);
document.body.appendChild(document.createElement("br"));
}
// modelテスト
var chat = new Chat("sample");
var chatElement1 = new ChatElement("hi!")
var chatElement2 = new ChatElement("how are you?");
var chatElement3 = new ChatElement("I'm too bad!");
var viewMock = new ViewMock();
chat.addView(viewMock);
chat.add(chatElement1);
chat.add(chatElement2);
chat.add(chatElement3);
assertEqual("chat add function test ", chat.getElements()[0], chatElement1);
assertEqual("chat add function mock test ", viewMock.getCounter(), 3);
chat.remove(chatElement2.id);
assertEqual("chat remove function test", chat.getElements()[1], chatElement3);
assertEqual("chat remove function mock test ", viewMock.getCounter(), 4);
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="chatModel.js"></script>
<script src="chatModelTest.js"></script>
</body>
</html>
chatModelTest.
ビューとコントローラを書く
まずはViewを書きましょう。
(function(global){
// DOM構築のための、簡易関数
function E(name,children){
var elm = document.createElement(name);
for(var i in children ){
elm.appendChild(children[i]);
}
return elm;
}
function Text(nodeValue){
return document.createTextNode(nodeValue);
}
var ChatFormView = function(){
this.input = E("input",[]);
this.button = E("button",[Text("メッセージを入力")])
this.node = E("div",[
this.input,
this.button
]);
}
var ChatElementView = function(element){
this.elementId = element.id;
this.removeButton = E("button",[Text("削除")]);
this.node = E("div",[
E("p",[Text(element.getText())]),
this.removeButton
]);
}
var ChatView = function(model){
this.form = new ChatFormView();
this.chatContentDiv = E("div",[]);
this.node = E("div",[
this.form.node,
this.chatContentDiv
]);
}
ChatView.prototype.setChatModel = function(chatModel){
this.chatModel = chatModel;
chatModel.addView(this);
}
ChatView.prototype.setController = function(controller){
this.controller = controller;
}
ChatView.prototype.update = function(){
for (var i = this.chatContentDiv.childNodes.length - 1; i >= 0 ; i-- ) {
this.chatContentDiv.removeChild(this.chatContentDiv.childNodes[i]);
}
var self = this;
var elementViews =
this.chatModel.getElements().map(function(m){
var chatElementView = new ChatElementView(m);
new ChatElementController(chatElementView, self.controller);
return chatElementView;
});
for(var i in elementViews){
this.chatContentDiv.appendChild(elementViews[i].node);
}
}
global.ChatView = ChatView;
}(window));
View自体の重要な点は、
そして、
(function(global){
var ChatElementController = function(view, parentController){
var self = this;
view.removeButton.onclick = function(e){
parentController.removeChatElement(view.elementId);
}
}
var ChatController = function(chatView,chatModel){
this.view = chatView;
this.model = chatModel;
chatView.setChatModel(chatModel);
chatView.setController(this);
var inputForm = chatView.form.input;
var self = this;
chatView.form.button.onclick = function(e){
var text = inputForm.value;
self.addChatElement(text);
}
}
ChatController.prototype.addChatElement= function(text){
this.model.add(new ChatElement(text));
}
ChatController.prototype.removeChatElement = function(elementId){
this.model.remove(elementId);
}
global.ChatElementController = ChatElementController;
global.ChatController = ChatController;
}(window));
オフラインのチャットを完成させる
それではオフラインのチャットを完成させましょう。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="chatModel.js"> </script>
<script src="chatView.js"></script>
<script src="chatController.js"></script>
<script>
var model = new Chat();
var view = new ChatView();
var contoroller = new ChatController(view,model);
document.body.appendChild(view.node);
</script>
</body>
</html>
このindex.
Milkcocoaでリアルタイムチャットを完成させる
ここまで来たら実際にMilkcocoaを組み込んで、
MVCにどの部分にMilkcocoaの通信を置けば良いのでしょうか? それはモデルです。モデルにMilkcocoaのdataStoreのリアルタイム通信を追加して行きましょう。
(function(global){
var Model = function(){};
Model.prototype.addView = function(view){
this.view = view;
}
Model.prototype.notify = function(){
if( this.view != null ){
this.view.update();
}
}
var ChatElement = function(text, id){
this.text = text;
this.id = id;
if( id === undefined ){
this.id = Date.now();
}
}
ChatElement.prototype = new Model();
ChatElement.prototype.getText = function(){
return this.text;
}
var Chat = function(dataStore){
this.elements = [];
this.id = Date.now();
this.dataStore = dataStore;
var self = this;
dataStore.on("push",function(d){
var text = d.value.text;
var id = d.id;
self.elements.push(new ChatElement(text,id));
self.notify();
});
dataStore.on("remove",function(d){
var id = d.id;
for(var i in self.elements){
if( self.elements[i].id === id ){
self.elements.splice(i,1);
self.notify();
return;
}
}
});
}
Chat.prototype = new Model();
Chat.prototype.getName = function(){ return this.name; }
Chat.prototype.getElements = function(){ return this.elements; }
Chat.prototype.add = function(chatElement){
this.dataStore.push({
text : chatElement.text
});
}
Chat.prototype.remove = function(id){
this.dataStore.remove(id);
}
global.Chat = Chat;
global.ChatElement = ChatElement;
}(window));
ここで、
チャットを完成させる。
最後にチャットを完成させましょう。index.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="http://cdn.mlkcca.com/v0.2.8/milkcocoa.js"></script>
<script src="onLineChatModel.js"> </script>
<script src="chatView.js"></script>
<script src="chatController.js"></script>
<script>
var milkcocoa = new MilkCocoa("https://io-pi6n3c5ul.mlkcca.com");
var datatStore = milkcocoa.dataStore("chat");
var model = new Chat(datatStore);
var view = new ChatView();
var contoroller = new ChatController(view,model);
document.body.appendChild(view.node);
</script>
</body>
</html>
これでチャットアプリケーションは完成しました。
まとめ
第3回は、