9564


![画面遷移とUIの図](https://i.stack.imgur.com/Cft4i.png)

左画面では、新しい動物を追加することができるようになっていて、追加した要素をタップすると右画面に切り替わるようになっています。右画面では、追加した要素のプロパティである名前、画像、解説を編集できるようになっており、OKボタンを押すと左画面に戻るようになっています。

この2つの画面の切替はJavascriptで行っており、ブラウザの更新は行われません。

Knockout.jsでは、配列をobservableArrayとしてViewModelに登録し、リストのタグに`data-bind='foreach:
animals'`を定義することで「表示」のことを意識しながらプログラムを組み立てなければならないことから開放されます。

とても便利なのですが、現在、編集画面（右画面）のDOMはひとつしか用意しておらず、タップした要素によって右画面のinput要素を再バインドさせる必要があり、どうすればいいのか困っています。

要素の数だけ編集画面（右画面）を用意すれば解決することなのかもしれません。しかし、画像入力のプログラムには画像をトリミングする機能などが含まれており、もし要素が100個存在していた場合に大量のインスタンスが作成されることを考えると、あまりよい方法では無いと思っています。

現在、作成しているViewModelはひとつだけで、画面全体をバインドの対象にしています。これを右画面と左画面でふたつのViewModelに分割し、要素がタップされたときに右画面のViewModelを破棄し新たにバインドし直すということも考えてみたのですが、`data-
bind`の値をjavascriptから動的に書き換えた後に`ko.applyBindings`に右画面のDOMを指定して再バインドすることに抵抗がありました。恐らく、もっとよい方法があるはずだと思ったからです。（そもそも一度バインドするとテンプレートが破壊されるような気もしますが）

Kockout.jsで複数の要素を同じinput要素に再バインドさせるにはどうすればよいのでしょうか？  
また、再バインドすることなくこの問題を解決出来る方法などございましたら是非教えていただきたいです。


CC BY-SA 3.0
2015-04-26T07:21:33.393
9561
2015-04-26T08:56:08.600
2015-04-26T07:39:44.367
8351
8351
question
javascript knockout.js
Knockout.jsで複数の要素を同じinput要素に再バインドさせたい


ViewModel全体を作りなおさない方法として、大きく分けて2種類あるかと思います。

  * 編集画面からanimalsの一要素にバインドしてしまう
  * 編集画面のobservableに対象要素をコピーし、編集が終わったらanimalsに反映

それぞれ具体例を1つずつ紹介します。

## 編集画面からanimalsの一要素にバインドしてしまう

編集画面側にAnimalを入れるobservableを用意しておき、そこから間接的に各プロパティをバインドします。Animalを一覧と編集画面で共有しているので、編集は直ちに一覧にも反映されます。この挙動はキャンセルを許可するときには面倒かもしれません。あとobservableが大量に生まれます。

[code]

    var animal = {name: ''};
    var default_animals = [{name: 'ポチ'}, {name: 'タマ'}, {name: 'ミケ'}];
    
    function ViewModel() {
      var self = this;
      
      self.animals = ko.mapping.fromJS(default_animals);
      self.current = ko.observable(ko.mapping.fromJS(animal));
      
      self.beginEdit = function(item) {
        self.current(item);
      };
    }
    
    ko.applyBindings(new ViewModel());
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
    <ul data-bind="foreach: animals">
      <li data-bind="text: name, click: $root.beginEdit"></li>
    </ul>
    <input data-bind="value: current().name" /><button>OK</button>
[/code]

## 編集画面のobservableに対象要素をコピーする

編集画面に遷移する際に、選択したAnimalの中身を編集画面側にコピーし、編集画面を抜ける時にanimalsに戻します。indexを保持する必要があるのと、observableArrayの部分更新が若干トリッキーなのが難点でしょうか。

[code]

    var animal = {name: ''};
    var default_animals = [{name: 'ポチ'}, {name: 'タマ'}, {name: 'ミケ'}];
    
    function ViewModel() {
      var self = this;
      
      self.animals = ko.observableArray(default_animals);
      self.currentIndex = null;
      self.current = ko.mapping.fromJS(animal);
      
      self.beginEdit = function(item, event) {
        ko.mapping.fromJS(item, self.current);
        self.currentIndex = ko.contextFor(event.target).$index();
      };
      self.endEdit = function() {
        self.animals.splice(self.currentIndex, 1, ko.mapping.toJS(self.current));
      };
    }
    
    ko.applyBindings(new ViewModel());
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
    <ul data-bind="foreach: animals">
      <li data-bind="text: name, click: $root.beginEdit"></li>
    </ul>
    <input data-bind="value: current.name" /><button data-bind="click: endEdit">OK</button>
[/code]


CC BY-SA 3.0
2015-04-26T08:56:08.600
9564
2015-04-26T08:56:08.600
8000
9561
answer