これは KnockoutJS Advent Calendar 2014 の9日目(12/9)の投稿です。
以前に sukobuto さんのこの記事(KnockoutJS の便利なプラグイン)を拝見し、ko.editables オモシロそうやん! と思ったので、この機に試してみました。導入手順から記載しますので、もし興味がある方は参考にしていただければと思います。
ちなみに Knockout.js ですが、薄いし学習コストも低いしで、さくっとWEB画面に動きをつけるにはオススメです。同じ MVVM のフレームワーク(ていうかライブラリ?)として Vue.js がありますが、これはレガシーブラウザに対応していないので、業務で Vue.js はまだ導入できないかな、という感じです。(Knockout.js はレガシーブラウザに対応しているので安心。)
また自分の場合は、Knockout.js と Rails を組み合わせたりしています。Rails とわりと相性がいいんじゃなかろうか、と思っています。参考までに、Rails と組み合わせる方法についてはこちら。
⇒ Rails⇔JavaScriptフレームワーク間でデータを受け渡す方法
まず Konockout.js の環境づくり
今回の方針として、Yeoman の webapp ジェネレータでプロジェクトの雛形を作成して、その後に Bower で Knockout.js と ko.editables
を導入することにします。
前提となる環境
OS:Mac OS X 10.9.5
yo:1.3.2
generator-webapp:0.5.1
また結果的に、Knockout.js のバージョンは 3.2.0
、ko.editables のバージョンは 0.9
になりました。
手順
webapp ジェネレータを起動します。(ちなみに CoffeeScript で書きます。)
$ yo webapp --coffee
今回は Bootstarap のみ選択しました。
雛形生成が終わったら、Bower で Knockout.js を導入します。
$ bower install knockoutjs --save
このバージョンの webapp ジェネレータだと、下記のコマンドで js/css ライブラリを index.html
へ自動で追記してくれます。
$ grunt wiredep
ここで一旦、Knockout.js の動作確認をしてみます。
ViewModel を書く
$ ->
HogeViewModel = (name, age) ->
self =
name: ko.observable(name)
age: ko.observable(age)
message: ko.observable("")
say_hello: ->
self.message("こんにちわ! " + self.name() + " さん。")
return
self
if $("#main")[0]
ko.applyBindings HogeViewModel("山田", 32), $("#main")[0]
return
View を書く
〜
<div id="main">
<div class = "form-group">
名前:<input class="form-control" data-bind="textInput: name">
年齢:<input class="form-control" data-bind="textInput: age">
</div>
<p>
<button class='btn btn-primary' data-bind='click: say_hello'>
<span data-bind="text: name"></span>さん <span data-bind="text: age"></span> 歳に挨拶!
</button>
</p>
<p style="color: goldenrod">
<span data-bind="text: message"></span>
</p>
</div>
〜
Knockout.js バージョン
3.2
からtextInput
属性が追加されたようです。入力をリアルタイムにキャッチしてくれます。
ファイルを保存したら、コマンドラインで $ grunt serve
でローカルサーバを立ち上げて、データバインドが動作していればOKです。
ko.editables を導入する
次に ko.editables を Bower で導入します。
他にも Knockout.js のプラグインは、こちらにまとめられています。色々ありますね。
⇒ https://github.com/knockout/knockout/wiki/Plugins
$ bower install knockout-editables --save
$ grunt wiredep
では自動で追記してくれなかったので、index.html
に次のように追記します。
〜
<!-- build:js(.) scripts/vendor.js -->
<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/knockoutjs/dist/knockout.js"></script>
<!-- endbower -->
+<script src="bower_components/knockout-editables/ko.editables.js"></script>
<!-- endbuild -->
〜
本題:ViewModel をロールバックしてみる!
デモをこちらに用意しました。
⇒ http://hkusu.github.io/Knockout.js_ko.editables_demo/dist/
イメージ:
[元に戻す]ボタンを押下すると、入力した内容がナシになります。[保存する]ボタンで一時セーブが出来ます。
ソースは次のようになっています。
ViewModel
もう少しすっきりした書き方ができるような気がしつつ。
$ ->
HogeViewModel = (name, power) ->
# トランザクション開始用の内部関数
begin_edit = ->
self.name.beginEdit()
self.power.beginEdit()
return
# コミット用の内部関数
commit = ->
self.name.commit()
self.power.commit()
return
# ロールバック用の内部関数
rollback = ->
self.name.rollback()
self.power.rollback()
return
# ViewModel のオブジェクト本体
self =
name: ko.observable(name) # 変数「name」を監視対象へ
power: ko.observable(power) # 変数「power」を監視対象へ
cancel: -> # 「元に戻す」ボタンが押下された際のアクション
rollback() # ロールバック
begin_edit() # 再度トランザクションを開始
return
save: -> # 「保存する」ボタンが押下された際のアクション
commit() # コミット
begin_edit() # 再度トランザクションを開始
return
# トランザクションの対象へ
ko.editable(self)
# 初期状態でトランザクション開始状態へ
begin_edit()
# リターン値としてオブジェクトを返す
return self
# View へ ViewModel をバインド
if $("#main")[0]
ko.applyBindings HogeViewModel("山田", 53), $("#main")[0]
return
View
<div id="main">
<div class = "form-group" style="line-height: 40px">
名前:<input class="form-control" data-bind="textInput: name">
年齢:<input class="form-control" data-bind="textInput: power">
</div>
<p style="color: goldenrod">
<span data-bind="text: name"></span> の戦闘力は <span data-bind="text: power"></span> 万です。
</p>
<button class='btn btn-primary' data-bind='click: cancel'>元に戻す</button>
<button class='btn btn-primary' data-bind='click: save'>保存する</button>
</div>
使いこなせば、これは便利なんじゃないでしょうか。気になることとしては、ko.editables
は数年メンテナンスされていないことですが、ソースは1ファイルのみのようなので、何かあれば自分で拡張しちゃうのも手かなと。
⇒ https://github.com/romanych/ko.editables/blob/master/ko.editables.js
おわりに
今回のソースはこちらに置きました。
⇒ https://github.com/hkusu/Knockout.js_ko.editables_demo
もしサーバと組み合わせるなら、ViewModel を commit したら、サーバ側のデータも保存するようにすればいいのかなと。
書いてて思ったのですが、ViewModel を保存 ⇒ サーバ側のデータを更新 のアーキテクチャ良いかも。面白そうなので、今後 Rails と組み合わせる実験をしてみようかな。
明日(12/10)は @tan_go238 さんによる ko.utils の記事です!よろしくお願いします。
あと、Knockout.js を TypeScript で、というタイトルで TypeScript Advent Calendar 2014 の 11日目に書く予定なので、もし興味があればそちらもご覧ください。