LoginSignup
4
5

More than 5 years have passed since last update.

[Knockout.js] ko.editables で ViewModel をロールバック/コミットしてみる

Last updated at Posted at 2014-12-08

これは 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 の環境づくり

今回の方針として、Yeomanwebapp ジェネレータでプロジェクトの雛形を作成して、その後に 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

スクリーンショット 2014-12-08 15.12.05.png

今回は Bootstarap のみ選択しました。

雛形生成が終わったら、Bower で Knockout.js を導入します。

$ bower install knockoutjs --save

このバージョンの webapp ジェネレータだと、下記のコマンドで js/css ライブラリを index.html へ自動で追記してくれます。

$ grunt wiredep

ここで一旦、Knockout.js の動作確認をしてみます。

ViewModel を書く

app/scripts/main.coffee
$ ->

  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 を書く

app/index.html
<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です。

スクリーンショット 2014-12-08 15.28.46.png

ko.editables を導入する

次に ko.editables を Bower で導入します。

他にも Knockout.js のプラグインは、こちらにまとめられています。色々ありますね。
https://github.com/knockout/knockout/wiki/Plugins

$ bower install knockout-editables --save

$ grunt wiredep では自動で追記してくれなかったので、index.html に次のように追記します。

app/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/

イメージ:

スクリーンショット 2014-12-09 11.10.56.png

[元に戻す]ボタンを押下すると、入力した内容がナシになります。[保存する]ボタンで一時セーブが出来ます。

ソースは次のようになっています。

ViewModel

もう少しすっきりした書き方ができるような気がしつつ。

app/scripts/main.coffee
$ ->

  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

app/index.html
<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日目に書く予定なので、もし興味があればそちらもご覧ください。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5