Backbone.jsで簡単なチャットを作ってみる

MVCモデルなjavascriptフレームワークBackbone.jsで超簡単なチャットを作ってみる。
とりあえずものは試しってことでかなり適当な実装だけど勘弁。バックエンドはGAE。
デモ(動作するのはchromeだけっぽい):http://ninsoeki-lab.appspot.com/ (現在停止中)

HTML

まず最初はHTMLを作る。backbone.jsを動かすためにはjQueryとunderscore.jsが必要。

<!DOCTYPE HTML>
<html>
<head>
	<meta charset="UTF-8">
	<title>Simple Chat</title>
	<link type="text/css" href="/css/main.css" media="all" rel="stylesheet"/> 
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
	<script type="text/javascript" src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
	<script type="text/javascript" src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
</head>
<body>
    <div id="container">
		<div id="chatArea"></div>
		<form method="POST" id='chatForm' name="newMessage" onsubmit="return false"> 
			<input name= 'newMessageString' type="text" /> <br/>
			<input type="submit" value='send'/> 
		</form> 
	</div>
</body>
	<script type="text/javascript" src="/js/application.js"></script>
</html>

今回はテンプレートは使ってないけど、大体の雰囲気は公式デモのコードを見るとわかるね。

    <script type="text/template" id="stats-template"> 
      <% if (total) { %>
        <span class="todo-count">
          <span class="number"><%= remaining %></span>
          <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
        </span>
      <% } %>
      <% if (done) { %>
        <span class="todo-clear">
          <a href="#">
            Clear <span class="number-done"><%= done %></span>
            completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span>
          </a>
        </span>
      <% } %>
    </script> 

Javascript

まずメッセージを保持するモデルを作る。

var Message = Backbone.Model.extend({});

constructor/initializeも定義できるけど、とりあえずこれだけでOK。
このモデルを格納するコレクションを作る。
urlプロパティを定義するとcreateはPOSTで、readはGETでそこにアクセスしてくれる。

var MessageStore = Backbone.Collection.extend({
	model: Message,
	url: function() {
		return window.document.URL + 'messages';
	}
});
var messages = new MessageStore;

submitされたときのハンドラー、レンダーをViewに定義。
あんまMVCっぽくないけど・・・。

var ProductView = Backbone.View.extend({
	events: {'submit #chatForm' : 'handleNewMessage' },
		
	handleNewMessage: function(data) {
		var inputField = $('input[name=newMessageString]');
		messages.create({
			content: inputField.val()
		});
		inputField.val('');
	},
		
	render: function() {
		var that = this;
		var result = ''
		messages.each(function(e, idx) {
			var template = '';
			if (idx % 2 === 1) {
				template = '<p class="top">' + e.get('content') + '</p>';
			}else {
				template = '<p>' + e.get('content') + '</p>';
			}	
			result += template.html();
		});
		$('#chatArea').html(result);
		return this;
	}
});

createが成功したときの処理をバインド。

messages.bind('add', function(message) {
	messages.fetch({
		success: function(){
			view.render();
		}
	});
});

elで指定された要素にrenderの結果が反映されることになる。
今回はあんまり関係ない。

var view = new ProductView({
	el: $('#container')
});

以上。

application.js
var Message = Backbone.Model.extend({});
var MessageStore = Backbone.Collection.extend({
	model: Message,
	url: function() {
		return window.document.URL + 'messages';
	}
});
var messages = new MessageStore;

messages.bind('add', function(message) {
	messages.fetch({
		success: function(){
			view.render();
		}
	});
});

var ProductView = Backbone.View.extend({
	events: {'submit #chatForm' : 'handleNewMessage' },
		
	handleNewMessage: function(data) {
		var inputField = $('input[name=newMessageString]');
		messages.create({
			content: inputField.val()
		});
		inputField.val('');
	},
		
	render: function() {
		var that = this;
		var result = ''
		messages.each(function(e, idx) {
			var template = '';
			if (idx % 2 === 1) {
				template = '<p class="top">' + e.get('content') + '</p>';
			}else {
				template = '<p>' + e.get('content') + '</p>';
			}
				
			result += template + '\n';
		});
		$('#chatArea').html(result);
		return this;
	}
});

var view = new ProductView({
	el: $('#container')
});

messages.fetch({
	success: function(){
		view.render();
	}
});

setInterval(function(){
	messages.fetch({
		success: function(){
			view.render();
		}
	});
},10000)

Python(GAE)

GET/POSTの処理を行うだけ。

class Message(db.Model):
    content = db.StringProperty(required = True)
    date = db.DateTimeProperty(auto_now_add = True)
	
class MessageHandler(RequestHandler):
	def post(self):
		json = simplejson.loads(self.request.form.get('model'))
		content = json['content']
		
		message = Message(content = content)
		message.put()
		
		return Response('')
		
	
	def get(self):
		response = Response()
		response.headers['Content-Type'] = 'application/json'
		
		contents = []
		query = db.GqlQuery("SELECT * FROM Message ORDER BY date DESC LIMIT 10")
		for i in query:
			c = {'content' : i.content}
			contents.append(c)
		
		response.data = simplejson.dumps(contents)
		return response

ネタ元:A Backbone.js demo app (Sinatra Backend)