LoginSignup
14
16

More than 5 years have passed since last update.

Knockout.jsで動的に生成したhtmlに対してbindingする

Last updated at Posted at 2015-09-08

Knockout.jsで、Ajaxで取得したhtmlに対してbindingしたい場合がありました。
うまくいったのでメモを残しておきます。

どうしてそんなことをしたくなったのか?

新規作成と編集のフォームを作りたかったのだけれど、Railsのモデルが複雑に絡んだformをKnockout.jsで制御するのはちょっと難しかったため(Knockout.jsを介してのformの投稿先、methodの変更などは面倒かなと…)、なるべくRailsの世界で完結するようにしたかったので動的にformを取ってくることにしました。

しかし、フォームの簡易的な検証機能はKnockout.jsを使いたい、という感じです。

簡単にはできない

シンプルに$.getでformを取得してしまえば成立するかと思いましたが、これだとbindingは有効になりませんでした。

<div id="form-memo" data-binding="with: formMemoViewModel">
</div>
formを取ってくる処理
$.get Routes.new_memo_path(), (data) ->
  $('#form-memo').html(data)
ViewModelをバインドする処理
class @FormMemoViewModel
  constructor: ->
    # なんらかの処理

$ ->
  window.appViewModel = {
    formMemoViewModel: new FormMemoViewModel()
  }
  ko.applyBindings(window.appViewModel)

流石に再bindingする必要がありそうです…。

カスタムバインディングを作る

ググったら、ヒントがありました。
Stack Overflow: knockout data-bind on dynamically generated elements

上記URLの一番下に書かれていたバインディングを作ればよさそうです。
要は、htmlを反映したタイミングでko.applyBindingsToDescendantsを実行して現在のBindingContextに追加する感じなのですが、それをobservableに反映したタイミングで行うものです。

CoffeeScriptで書き直したのが以下になります。

html_with_binding.js.coffee
ko.bindingHandlers.htmlWithBinding = {
  init: ->
    controlsDescendantBindings: true
  update: (element, valueAccessor, allBindings, viewModel, bindingContext) ->
    element.innerHTML = ko.utils.unwrapObservable(valueAccessor())
    ko.applyBindingsToDescendants(bindingContext, element)
}

使ってみる

htmlにはAjaxで取得したhtmlを入れるobservable(formHtml)をカスタムバインディング付きで定義します。

<div id="form-memo" data-binding="with: formMemoViewModel">
  <div data-bind="htmlWithBinding: formHtml"></div>
</div>

formを取得する処理では、取得したhtmlをobservableに入れます。HTMLに直では入れません。

formを取ってくる処理
$.get Routes.new_memo_path(), (data) ->
  window.app_view_model.formMemoViewModel.formHtml(data)
ViewModelをバインドする処理
class @FormMemoViewModel
  constructor: ->
    @formHtml = ko.observable()
    # なんらかの処理

$ ->
  window.appViewModel = {
    formMemoViewModel: new FormMemoViewModel()
  }
  ko.applyBindings(window.appViewModel)

このようにしたところ、Ajaxで取得したhtmlに対してもKnockout.jsのbindが有効になりました。よかったよかった。

14
16
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
14
16