【spine.js】Spineのドキュメント翻訳 Models
公開日:
:
最終更新日:2012/08/05
spine.js
元のドキュメントはこちら
はじめに
状態が変化する中、クライアントサイドで実現したいのはデータ管理です。ステートフルJavaScriptでのデータ管理は従来のサーバサイドアプリケーションの通常のやり方とは全く違います。リクエスト/レスポンスモデルはなく、サーバサイドの変数にアクセスすることはありません。そのかわりデータはリモートで取得され、クライアントサイドに一時的に保持されます。これはデータアクセスが直接アクセスされることに利点があり、あるとしても、ごくまれにデータのロードを待つことがある程度です。
初期ページがロードされると、リモートデータはmodelsと呼ばれるクラス構造でローカルに保存されます。modelsはSpineの中核で、アプリケーションの重要な部分です。アプリの全データを保持するだけでなく、データに関連付けられたロジックも保持しています。
Modelsはアプリのその他の部分と分離され、完全に独立すべきです。ModelデータはHTML5ローカルストレージかAjaxで永続化されます。
実装
ModelはSpine.Modelの継承で作成されます。
var Contact = Spine.Model.sub(); Contact.configure("Contact", "first_name", "last_name");
変数やイベントを開始するために、まずはじめにconfigure()を呼ばなくてはなりません。configure()にmodel名とmodelが持つ属性名を渡します。
modelsは他のCoffeScriptクラスと似ていて、クラス/インスタンスメソッドを同様の方法で加えることができます。
var Contact = Spine.Model.sub(); Contact.configure("Contact", "first_name", "last_name"); Contact.extend({ filter: function(query) { return this.select(function(c){ return c.first_name.indexOf(query) != -1 }); } }); Contact.include({ fullName: function(){ return(this.first_name + " " + this.last_name); } });
modelsはSpineモジュールなのでそれど同様にextendやincludeすることができます。
var Contact = Spine.Model.sub(); Contact.configure("Contact", "first_name", "last_name"); Contact.extend(MyModule);
modelインスタンスはnewで作成され、オプションで属性のセットを渡せます。
var contact = new Contact({first_name: "Alex", last_name: "MacCaw"}); assertEqual( contact.fullName(), "Alex MacCaw" );
また、簡単にサブクラスを作成することもできます。
var User = Contact.sub(); User.configure("User");
レコードの保存と取得
一度インスタンスが作成されるとsave()を呼ぶことでメモリに保存することができます。
var Contact = Spine.Model.sub(); Contact.configure("Contact", "first_name", "last_name"); var contact = new Contact({first_name: "Joe"}); contact.save();
レコードが保存される時、IDがなければSpineは自動でそれを付加します。
assertEqual( contact.id, "AD9408B3-1229-4150-A6CC-B507DFDF8E90" )
find()を使って保存されたレコードを取得するときにこのIDを利用することができます。
identicalContact = Contact.find( contact.id ) assert( contact.eql( identicalContact ) )
レコードの属性を変更すると、再度save()を呼ぶことでメモリ上のデータを更新することができます。
var contact; contact = Contact.create({ first_name: "Polo" }); contact.save(); contact.first_name = "Marko"; contact.save();
first()やlast()を使って最初や最後のレコードを取得することができます。
var firstContact; firstContact = Contact.first();
全てのレコードを得るためにall()を使います。
var contact, contacts, _i, _len; contacts = Contact.all(); for (_i = 0, _len = contacts.length; _i < _len; _i++) { contact = contacts[_i]; console.log(contact.name); }
each()を使って、全データに渡って繰り返す関数を渡すことができます。
Contact.each(function(contact) { return console.log(contact.first_name); });
また、select()でレコードの一部を選択することもできます。
Contact.select(function(contact) { return contact.first_name; });
バリデーション
modelの確認は簡単です。単純にvalidate()関数をオーバーライドします。
var Contact = Spine.Model.sub(); Contact.configure("Contact", "first_name", "last_name"); Contact.include({ validate: function(){ if ( !this.first_name ) return "First name is required"; } });
もしvalidate()が何かを返したらバリデーションは失敗し、errorイベントが発生します。イベントリスニングすることでこれをキャッチしユーザーに知らせることができます。
Contact.bind("error", function(rec, msg) { return alert("Contact failed to save - " + msg); });
加えて、save()やcreate()、updateAttribeutes()もバリデーションの失敗したらfalseを返します。バリデーションについてより詳細はバリデーションガイドを御覧ください。
シリアライゼーション
SpineのmodelはJSONシリアライゼーションの特別なサポートを持っています。レコードのシリアライズのために、JSON.stringify()にレコードを渡して呼びます。全レコードをシリアライズするにはmodelを渡します。
JSON.stringify(Contact) JSON.stringify(Contact.first())
代わりにattributes()を呼ぶことで、インスタンスの属性を取得し、独自のシリアライゼーションを実装することができます。
var contact; contact = new Contact({ first_name: "Leo" }); assertEqual(contact.attributes(), { first_name: "Leo" }); Contact.include({ toXML: function() { return serializeToXML(this.attributes()); } });
もしネイティブなJSONをサポートしていない古いブラウザ(例:IE7)を使用している場合は、json2.jsを読み込んでレガシーサポートして下さい。
永続化
レコードがメモリ上に保持されている間は素早く取得するのに便利ですが、いずれにせよたいていは永続化が必要とされます。AjaxやHTML5ローカルストレージなど、Spineはいくつかの永続化のためのストレージモジュールを用意しています。詳細はAjaxとローカルストレージのガイドを御覧ください。
イベント
errorやajaxErrorなど、modelが自身に関連付けられたいくつかのイベントを持っていることを既に見てきましたが、create/update/destroy の実行のコールバックについてはどうでしょうか。便利なことにSpineはこれらも用意しており、以下のイベントにバインドすることができます。
イベント:
- save – レコードが保存された(create/updatedも含む)
- update – レコードが更新された
- create – レコードが作成された
- destroy – レコードが削除された
- change – 上記いづれか、つまりレコードが作成、更新、削除された
- refresh – 全レコードが無効化され置き換えられた
- error – バリデーションに失敗
例えば、modelのcreateイベントには以下のようにバインドできます。
Contact.bind("create", function(newRecord) {});
コールバックのために関連付けられたレコードがいつもコールバックに渡されます。
その他のオプションとして、特定のレコードに直接イベントをバインドできます。
var contact; contact = Contact.first(); contact.bind("save", function() { return updateInterface(); });
コールバックのコンテキストはリスナが置かれたレコードになります。modelイベントはレコードをビューにバインドしデータとビューが同期しているのを見て、modelイベントが重要であることが分かるとおもいます。
もしイベントを削除したい場合、unbin()を呼ぶことで特定のイベントをアンバインドすることができます。unbind()の使用について詳細はイベントドキュメントを御覧ください。modelインスタンスはまたunbind()関数を持っていますが、これは各インスタンスの全てのイベントリスナを削除する場合にのみ使用できます。
ダイナミックレコード
より巧みなSpineのmodelはダイナミックレコードで、これはプロトタイプの継承を使用しています。find()、all()、fitst()、last()などが呼ばれると、modelイベントコールバックは保存レコードのクローンを返します。これはレコードが更新されればいつでもクローンに瞬時に更新が反映されることを意味します。
それではコードサンプルをお見せしましょう。ここではassetとそのクローンを作成しようとしています。assetを更新するとクローンもまた自動的に更新されることがわかります。
var asset, clone; asset = Asset.create({ name: "whatshisname" }); clone = Asset.find(asset.id); asset.updateAttributes({ name: "bob" }); assertEqual(clone.name, "bob");
これはreload()関数を呼ぶような面倒をしなくてよいことを示します。全てのインスタンスは常に保存されたバージョンと同期していることがわかります。
リレーションシップ
Spineはhas-many、has-one、belongs-toの関係をサポートしています。詳細はリレーションシップガイドを御覧ください。
APIドキュメント
modelについてのより詳細な情報はAPIドキュメントを御覧ください。
関連記事
-
-
【spine.js】Spine.jsの基本 ~クラスの操作~
このエントリでprivateなプロパティを宣言してクラスみたいなことをやっていたのだけれど、これだけ
-
-
【spine.js】Spineのドキュメント翻訳 Views & Templating using jQuery.tmpl
javascriptのテンプレートエンジンは他にMustacheというのが有力そうです。強みは様々な
-
-
【spine.js】Spineの使い方
Spine.jsを使ってみようということで基本となる使い方をメモ。 詳しい話はAPIがあるしチュー
-
-
【spine.js】Spineのドキュメント翻訳 Routing
元のドキュメントはこちら ↓↓↓ Routing - Documentation - Spi
-
-
【spine.js】Spine.jsを使ってはまったポイント
Spine.jsでクラスの継承をさせようとしていくつかはまってしましました。 ※CoffeSc
-
-
【spine.js】Spineのドキュメント翻訳 Controllers
元記事はこちら はじめに controllerはSpineの三位一体の最後の1つ