自分がプロジェクトでKnockoutを使うときに気をつけていること

れはKnockoutJSアドベントカレンダー24日目の記事です。


KnockoutJS Advent Calendar 2014 - Qiita


今日はネタも尽きたので自分がプロジェクトでKnockoutを使うときに気をつけていることをグダグダ書こうと思います。

1. ViewModelやModelにjQueryなどのDOM操作をするライブラリを使用しない

これは責務の問題とテストしやすくするためです。

KnockoutJSでは MVVM (Model-View-View Model) というデザインパターンを適用することでUIをシンプルに記述できるようにしています。


この "Model", "View", "View Model" という役割ですが、それぞれ


Model: データ自体を扱う

View: ユーザインターフェースを扱う

ViewModel: UIバインディングを行ったりモデルからデータ取得したり更新したりする


といったような役割分担があります。


例えばHTMLは基本的にViewレイヤー。あるデータに特化したロジックやサーバとの通信を用いた永続化処理などはModel(JS)に書きます。ViewModelはJSです。



ただ、たまにバインディング時にjQueryでDOM指定して何かやりたい場合があります。
そういう場合は自分はカスタムバインディングを使ったりしますが、基本的には使わないよう気をつけます。


こうすることで ViewModel はほぼPOJO?(Plain Old JavaScript Object)で書くことができます。
ただModelでサーバと通信をしたりします。その場合は jquery-mockjax を使ったりモックで代用します。



テストをセットアップについては以前ブログを書いたので参考にしてみるといいかもしれません。


Knockout.jsとKarmaで簡単TDD開発 - 418 | I'm a teapot



2. データを引数にModelを作成するときはコンバータを挟む

サーバからデータを取得しModelを作成するときですが、場合により形が違うデータ(JSオブジェクト)がくる場合があります。


例えば、以下のような業務システム(発注業務)を考えてみます。

  • 初期データ取得時、サーバから商品IDに紐付く商品情報を取得しModelを作成して発注明細に追加する
  • 商品検索ボタンを押すと検索ダイアログが開く。そのダイアログから商品を選択してModelを作成して発注明細に追加する
  • 商品追加ボタンを押すと空の商品情報入力フォームが発注明細に追加される。

この場合、同じモデル(商品情報)を作る場合でも場合によりModelを作成するときの引数に渡されるJSオブジェクトのデータ構造が変わる場合があります。


こういった場合、都度Model内で変換してもいいのですが、別途以下のようなコンバータみたいなのを作ってやると管理が楽になります。

var converter = {
        // 発注情報に登録されている商品情報を発注明細に追加したとき
	fromServer: function(item){
		return {
			productCode: item.order.product_code
			productName: item.order.product_name
                };
	},
        // 検索ダイアログから商品情報を発注明細に追加したとき
    	fromSearchDialog: function(item){
		return {
			productCode: item.productCode
			productName: item.productName
		};
	},
        // 空の商品情報入力フォームが発注明細に追加されたとき
	createEmpty: function(){
		return {
			productCode: null,
			productName: null
		};
	}

};


例えば初期データ取得時、サーバから商品IDに紐付く商品情報を発注明細に追加する場合は、
サーバから取得した商品情報のJSオブジェクトに converter.fromServer() を使います。

model.load("/product/1", {},
	function(result){
		var product = converter.fromServer(result);
		self.orderDetals.push(product);
	}
);


同様に検索ダイアログから商品情報を発注明細に追加する場合は以下のようにします。

function selectProduct(selectedProduct) {
	var product = converter.fromSearchDialog(selectedProduct);
	self.orderDetals.push(product);
}

上記はあくまでサンプルですがイメージとしてはこんな感じです。
そろそろ本格的にグダグダしてきたのでこれぐらいで・・・。


明日はいよいよラスト! @sukobuto さんの「KO + TypeScript + Cordova でハイブリッドアプリ開発」です!よろしくお願いします!