おはこんばんにちは。この記事はメモ的な意味合いが強いです。というか、完全にメモ。AngularJS採用すべきか悩んでます どうしたらいいんだ? というわけで、すごいためになる記事期待してたら、まじ、ゴメン、もし少し時間があったらざっと読んでみて、関連した情報をコメントとかで教えてもらえるとうれしいです。
あと、Node.jsベースのアプリケーションフレームワーク sails.jsに関するある程度の知識を持っている人が読む前提で書いてます。サンプルコードとか、リアルなコードは少なく思想メモに近いので、そういうの嫌いな人は先を読まないで、宗教論争は嫌い。
正解は無いと思うので指摘は大歓迎です。
何のためのメモか?
- sails.js でModel-View binding をうまくやるにはどうしたらいいの?について考える時のリソース(記事やら、リファレンスマニュアルやら)をまとめる。
いまどこ?
- sails.js 使ってたらブラウザjs側の Model-View binding の必要性に気づく(特に io.socket 周り)
- 最近の javascript について調査開始
- qiitaでメモ作成開始
- 「双方向バインディング」という思想を中心により深い調査へ
- AngularJSがいいらしい?と気づく
- AngularJSはクソだという批判も多いことに気づく
- どうしよう ←いまここ
経緯
- sails.js 使って io.socket ごりごり使うアプリケーションをかいていました
- io.socket でサーバーから取得したデータをうまく扱うにはどうすべき?
- ブラウザ js で綺麗に取り扱うにはどうすべき?
- sails.js の models 内のモデルをブラウザjsと共有したい ⇒ ソースの可読性向上とか一貫性とか
- モデルデータの変化とUI(純粋なHTMLとしてのビュー)の変化の間を取り持つものが欲しい⇒同じようなコードを何回も書きたくない
とか、そんなことを考えるようになり、そして
『io.socket がフレームワークに組み込まれてても、modelをブラウザjsと共有できないとか、つくった奴はばかぢぁねぇのか? 』
と思ったりしつつ、
『車輪の再発明はしたくないし、そもそも、楽したいんですけど・・・』
という怠惰な性格があった上で、ソフトウェア、特にオープンソースの世界は自分で貢献できる素敵な世界でもあるわけで、どうすればいいのか考えてる、それがいま。
※上に書いた通り、io.socketをごりごり使う特殊なものを取り扱っており、これは、ユーザーの操作によらず画面に表示されるものが変更されていく特徴をもったシステムを取り扱っているところによります。たとえば、チャットや株価の画面を想像するとわかりやすいかもしれません。
※ブラウザ.jsが直接データを取り扱うことのないシステムであれば、例えばサーバーサイドの view テンプレート .ejs で済むのであれば、こんなことを考える必要はそもそも無いのです。
最近のjsフレームワークやライブラリの流行り
- 現実的にはjQueryが圧倒的のように感じる
- backbone.js や AngularJS という新しい思想のものが出てきた
- ここ一年ぐらい(いまは2015年1月)で AngularJS の利用者がめちゃくちゃ増えたらしい(ソースは無い)
同じようなことを考え(ている|た)ひとはたくさんいる
jQuery における model-view binding について
jQuery binding data with view - Stack Overflow - http://goo.gl/Ek8PUW
I'm writing an app that is a 'power meter' of sorts where I show a list of 'locations' in the navigation that have a power rating associated with them, and a gauge that aggregates this information. Locations can be nested. Clicking a location changes my app's 'state' and loads its child locations into the nav. Clicking back takes user to previous locations listing.
回答
There are now official jquery plugins ( http://blog.jquery.com/2010/10/04/new-official-jquery-plugins-provide-templating-data-linking-and-globalization/ ) that provide data linking and templating. See what John Resig has to say about it all. Also, there are some other resources that you might be interested in:
http://github.com/nje/jquery-datalink
http://github.com/nje/jquery-datalink/wiki/jquery-data-linking-proposal
https://github.com/raid-ox/chain.js/wiki/ (this one is old, but might be worth looking at the code)
javascript - List all bindings of an element (with jQuery) - Stack Overflow - http://goo.gl/BPqB5a
Is there a way to list all bindings on a jQuery element? jQuery's bind() does only seem to attach them and I didn't find a jQuery function that does get the bindings.
$.each($._data("#element", "events"), function(i, e) {
console.log(i, e);
});
.bind() | jQuery API Documentation - http://goo.gl/Wgiw
backbone.js における model-view binding について
backbone.js 自体が薄いフレームワークでそれ自体に mode-view binding 機能は無い(無さそう)だが、連携するライブラリとしていくつか提供されている。
AngularJS における model-view binding について
AngularJS 自体が model-view binding の仕組みを備えている。
攻略!AngularJS - AngularJSとは | CodeGrid - https://app.codegrid.net/entry/angularjs-1#Headings-angularjs-1-2
もうひとつのAngularJSの大きな特徴は双方向データバインディングです。AngularJSにおける双方向データバインディングは、データとUIを双方向に結びつけ、UIが変更されれば自動的にデータが変更され*、データが変更されると自動的にUIが変更されます。
angular.js - AngularJSを選択する見解 - Qiita - http://goo.gl/ZtOV4d
- HTML側とController側の同期操作を意識する必要がない。
AngularJSのBindingの仕組みは、デモとかみたらすぐわかると思うのですが、
シンプルで強力で協力的ですよね。上記のサンプルHTMLでも書きましたが、Unixのパイプのような形でFilterの仕組みを利用できて、いうことなしです。
入力用UI
<!DOCTYPE html>
<html lang="ja" ng-app>
<head>
<meta charset="utf-8">
<title>AngularJS bindinig demo</title>
<script src="../lib/angular.min.js"></script>
</head>
<body>
<input type="text" ng-model="name">
<div>Hello {{name}}!</div>
</body>
</html>
Vue.js の model-view binding について
sails.js 関連で qiita.com をあさってたら見つけたので
Sails.js と Vue.js で API ファースト + リアルタイムな Web アプリケーションをさくっと作る - Qiita - http://goo.gl/EYHJSX
入力用UI
<ul>
<li v-repeat='messages'>{{body}}</li>
</ul>
<form v-on='submit: create'>
<input v-model='newMessage.body' type='text' placeholder='メッセージ (Enter で投稿)'>
</form>
イベントハンドラの登録例
var app = new Vue({
el: 'body',
data: {
messages: []
},
events: {
'message:created': function (message) {
this.messages.push(message);
}
},
created: function () {
var _this = this;
// サーバに GET /message としてリクエストする
io.socket.get('/message', function (res) {
_this.messages = res;
});
// io.socket.on でモデルの変更イベントを監視できる
io.socket.on('message', function (event) {
// event.verb が変更の種類を表す
switch (event.verb) {
case 'created': // created: モデルに新たなデータが追加された
_this.$emit('message:created', event.data);
break;
}
});
},
methods: {
create: function (event) {
event.preventDefault(); // submit 時のページ遷移を無効にする
var _this = this;
// サーバに POST /message としてリクエストする
io.socket.post('/message', this.newMessage, function (res) {
if (res.error) {
return console.error(res.error);
}
_this.$emit('message:created', res);
});
}
}
});
もう一度、双方向バインディングとかModel-View bindingの視点から整理したい
各種フレームワークを俯瞰してみる
【セミナー】天下一クライアントサイドJS MV*フレームワーク武道会 - Qiita - http://goo.gl/PW6Ayf
この記事は全体感が把握できる。id:konweb さん素敵。適当にまとめる。
フレームワーク | 特徴 | 所感 |
---|---|---|
AngularJS | フルスタック、単品で、いい感じまでいける | フルスタックだとごちゃごちゃしすぎなんじゃない?使いこなせないこともある?ほかのライブラリとの相性はどうなの? |
Vue.js | 超軽量、AngularJSの後に出てきたので、AngularJSの流れもくんでる | テストがしにくいとどこかに書いてあったけど、超軽量なのは魅力 |
knockoutjs | 双方向バインディングだ | 双方向バインディング押しなので気になる |
AngularJSが(2.0へのアップデートの話があるものの)かなり批判されているし、Vue.jsとknockoutjsが気になりだしてきた。
そもそも何がしたいの?
もろもろの整理をこのまま続けてると「お勉強楽しい」とか「知識がついている」だけの気分になってしまう、危機感 目的を整理しなおす。
これまで:jQueryからのDOM操作
サンプルコードは動作検証無し⇒文法ミスあるかも
<div id="mydiv"></div>
というhtmlがあったときに、例えば
【初日のjs】
$('#mydiv').append('<div>子要素ですよろしくね</div>');
という感じで、子要素を追加できる。jQueryまじ楽。hoge.htmlをview側、hoge.jsをmodel側と考えれば、model側からviewを参照してる。最近の流れはディレクティブ使って、view⇒modelという参照が主流の雰囲気
大雑把にいえば、『hoge.js 内にhtmlコード<div>子要素ですよろしくね</div>
あるのが汚い』⇒『分割して綺麗にしよう』、このコード(hoge.js)がいろいろ見た目の変更があって、2週間後には
【2週間後のjs】
$('#mydiv').append('<div class="myclass">子要素ですよろしくね</div>');
※見た目の修正のためにclass属性にmyclassが指定された
になり、その3日後には
【2週間と3日後のjs】
var child = document.createElement('div');
$(child)
.appendTo('#mydiv')
.class('myclass')
.click(function(event){
// do something for click
})
.html('<a href="show_child?id="' + childid + '>子要素</a>ですよろしくね');
※リンクが追加されて、クリック時のイベントで何かすることになった
になっているかもしれない。複雑さがまして、スパゲッティ一直線だ。業界ではこれを「ごりごり書く」といい、火消しプロジェクトを渡り歩いてきた人間は
js ごりごり書いて火消しプロジェクトに貢献しました
と言うかもしれない。誇らしい。そんな、マッチョプログラマーにはこのHTML
<!DOCTYPE html>
<html>
<head><script type='text/javascript' src='fire.js'></script></head>
</html>
をベースに何かつくってもらおう
view というかテンプレートを固定したい
分割するなら、view側、つまりhtmlをテンプレートとして固定したい(できるだけ)。
固定した時のメリット
- デザインとアプリケーション開発(動的なプログラミング部分)の分離⇒フレームワークとしてだけでなく、作業者・作業単位としても分離できる可能性
- 後々の変更箇所が明確になる⇒見た目のみの変更に対して、プログラミングソースを触らない
とかは大事。デメリットはもちろんあるが割愛。
でも、綺麗に分割したつもりでも、その後の変更でスクリプト側から静的な部分、例えばHTML構造、を操作しなければいけなくなったらどうするの?
と考えておくのは大事だと思う。たった今のテンプレートとしての複雑さが今後も維持されるとは限らないし、より複雑になるのが普通だ。
繰り返しをどうするのか?
配列とか、連続した何か?を取り扱わなければいけないってのは、面倒な部分だよね。要素内のデータ構造(プロパティ名とかそのデータ自体のルール)は誰が知っているべきなんだろうか?
繰り返しに関わるところはいつもぎこちない
- いくつ繰り返されるかわからない
- 最初の/最後の/偶数番目の/機数番目の、といった条件で何かが変わることが多い