web-dev-qa-db-ja.com

ノックアウトバインディングを使用してselectのイベントを変更します。実際の変更かどうかを知る方法はありますか

許可UIを作成しています。許可のリストがあり、各許可の横に選択リストがあります。許可は、選択リストにバインドされたオブジェクトの監視可能な配列で表されます。

<div data-bind="foreach: permissions">
     <div class="permission_row">
          <span data-bind="text: name"></span>
          <select data-bind="value: level, event:{ change: $parent.permissionChanged}">
                   <option value="0"></option>
                   <option value="1">R</option>
                   <option value="2">RW</option>
           </select>
      </div>
 </div>

問題はこれです。UIが初めて作成されたときにchangeイベントが発生します。私はajax関数を呼び出し、許可リストを取得してから、許可項目ごとにイベントを発生させます。これは本当に私が望んでいる動作ではありません。上げる必要がありますユーザーが実際に選択リストでアクセス許可の新しい値を選択した場合のみ、どうすればそれができますか?

81
guy schaller

実際にはイベントがユーザーまたはプログラムによってトリガーされたかどうかを確認したいであり、初期化中にイベントがトリガーされることは明らかです。

subscriptionを追加するノックアウトアプローチは、ほとんどの場合、モデルのほとんどがこのように実装されるため、すべての場合に役立つわけではありません。

  1. 未定義データを使用してモデルを初期化し、構造のみ(actual KO initilization)
  2. 初期データでモデルを更新する(logical init like load JSON , get data etc)
  3. ユーザーの操作と更新

キャプチャする実際のステップは3の変更ですが、2番目のステップではsubscriptionがcallを取得するため、より良い方法は次のようにイベントの変更に追加することです

 <select data-bind="value: level, event:{ change: $parent.permissionChanged}">

permissionChanged関数でイベントを検出しました

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}
91
Sarath

これは推測に過ぎませんが、levelが数字であるために起こっていると思います。その場合、valueバインディングはchangeイベントをトリガーして、levelをストリング値で更新します。したがって、levelが最初の文字列であることを確認することで、これを修正できます。

さらに、これを行うためのより「ノックアウト」な方法は、イベントハンドラーを使用せず、オブザーバブルとサブスクリプションを使用することです。 levelを監視可能にしてから、それにサブスクリプションを追加します。これは、levelが変更されるたびに実行されます。

32
Michael Best

この奇妙な動作に役立つ解決策を次に示します。変更イベントを手動でトリガーするボタンを配置するよりも良い解決策を見つけることができませんでした。

編集:このようなカスタムバインディングが役立つかもしれません:

ko.bindingHandlers.changeSelectValue = {

   init: function(element,valueAccessor){

        $(element).change(function(){

            var value = $(element).val();

            if($(element).is(":focus")){

                  //Do whatever you want with the new value
            }

        });

    }
  };

また、選択したデータバインド属性に以下を追加します。

changeSelectValue: yourSelectValue
4
Ingro

私はこのカスタムバインディングを使用します(RP Niemeyerによる this fiddleに基づいています- this 質問に対する彼の答えを参照)。これにより、数値が文字列から数値に適切に変換されます( Michael Bestのソリューションが示唆するとおり):

Javascript:

ko.bindingHandlers.valueAsNumber = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function () {
                    var val = ko.utils.unwrapObservable(observable);
                    return (observable() ? observable().toString() : observable());
                },
                write: function (newValue) {
                    observable(newValue ? parseInt(newValue, 10) : newValue);
                },
                owner: this
            });
        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

HTMLの例:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>
3
mhu

プリミティブ値の代わりにオブザーバブルを使用する場合、selectは初期バインディングで変更イベントを発生させません。オブザーバブルに直接サブスクライブするのではなく、変更イベントにバインドし続けることができます。

2
Stefan Smith

簡単なフラグを使用して、迅速かつ汚い:

var bindingsApplied = false;

var ViewModel = function() {
    // ...

    this.permissionChanged = function() {
        // ignore, if flag not set
        if (!flag) return;

        // ...
    };
};

ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true

これが機能しない場合は、setTimeout()で最後の行をラップしてみてください。イベントは非同期であるため、applyBindings()が既に返されたときに最後の行がまだ保留されている可能性があります。

1
Niko

Knockoutを使用して作業している場合、観察可能な機能ノックアウトの主要な機能を使用します。
ko.computed()メソッドを使用し、その関数内でajax呼び出しを実行してトリガーします。

0
vasu

同様の問題があり、変数の型を確認するためにイベントハンドラを変更しました。タイプは、ページが最初にロードされたときではなく、ユーザーが値を選択した後にのみ設定されます。

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

これは私にとってはうまくいくようです。

0
fireydude

これを使って:

this.permissionChanged = function (obj, event) {

    if (event.type != "load") {

    } 
}
0