ノックアウトバインディングを使用した選択時のイベントの変更、実際の変更かどうかを確認するにはどうすればよいですか


87

パーミッション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が初めて入力されたときに、変更イベントが発生することです。ajax関数を呼び出し、権限リストを取得すると、権限アイテムごとにイベントが発生します。これは本当に私が望む振る舞いではありません。ユーザーが選択リストアクセス許可の新しい値を実際に選択した場合にのみ発生させたいのですが、どうすればよいですか?

回答:


107

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

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

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

キャプチャしたい実際のステップは3の変更ですが、2番目のステップでsubscription呼び出しが発生するため、次のようにイベントの変更に追加することをお勧めします。

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

permissionChanged機能内のイベントを検出しました

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
これは正解としてマークされるべきだと思います。質問で説明されているとおりの正確なシナリオがあり、select要素へのキーとして文字列を渡すことをテストしました。ただし、selectの初期値がオプションの1つと一致した場合でも、changeイベントがトリガーされました。この単純なifブロックをハンドラーに実装すると、すべてが期待どおりに機能しました。最初にこの方法を試してください。ありがとう、@ Sarath!
pflugs 2015年

ユーザーの変更とプログラムの変更を区別するという概念が好きです。これは日常の場合、特にオブザーバブルがユーザーの操作なしで値を変更する可能性が最も高いノックアウトの場合に役立ちます。
ロディワ2016年

@Pflugsに同意し、イベントタイプを区別することでうまくいきました。これは、受け入れられる答えです。
エマミドルブルック2016

1
defは受け入れられた答えである必要があります...イベントタイププロパティは、premissionChangedのトリガーを区別するために使用できます
caniaskyouaquestion

1
また、このメソッドを使用して、ユーザーが別のトリガーではなく選択を変更していたかどうかを検出しました。私の場合、「options」バインディングを介してオプション値を更新すると、「change」イベントがトリガーされました。
nath 2017

32

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

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


ページの読み込み後にフォームが「変更」イベントをトリガーする原因を2時間掘り下げた後、あなたは私を救ったと推測しています。
Samih A 2015年1

あなたの答えは私にアイデアを与えました...私は意図的にURLから来るタイプを変更したので、サブスクライブ関数はページのロード時にトリガーされます(ドロップダウンが変更をトリガーしたときだけでなく、URL変数を処理したかった)。これを行うためのよりハッキーな方法はありますか?
Jiffy 2017

4

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

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

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

ああ...あなたは保存ボタンのような意味ですか?..私には良くありません..私は本当にこれが機能する必要があります:)
男schaller 2012年

より良い解決策で答えを編集してください(うまくいけば)。
イングロ2012年

ねえイングロ、これは答えではないとして(誤って)フラグが立てられました。私はあなたの最初の文を言い換えたので、フラガーはあなたが答えとして質問を投稿していると誤って思わないようにしています。お役に立てれば!:)
jmort253 2013年

@ jmort253:すみません、それは私のせいでした。「私も同じ問題を抱えています」と答えない方がいいでしょう。これはあなたが何か他のことを求めているように見えるからです。代わりに、回答の解決策を直接提供し、それがどのように機能するかを説明してください。
カンタス94ヘビー

1
うん、すみません、私の間違い。これは私がstackoverflowに投稿した最初の回答のひとつであり、すべてのルールを知りませんでした。編集してくれてありがとう!
イングロ2013年

3

私はこのカスタムバインディングを使用します(RP Niemeyerによるこのフィドルに基づいています。この質問に対する彼の回答を参照してください)。これにより、数値が文字列から数値に適切に変換されます(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>

2

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


1

シンプルなフラグを利用して、すばやく汚い:

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()がすでに返されている場合、最後の行はまだ保留中です。


こんにちは返信ありがとうございます。domの準備ができたときにko.applyBindingsを呼び出すため、これは機能しませんが、後の段階でパーミッションaがモデルに入力されます...したがって、フラグはtrueになりますが問題はまだ発生します。
ガイシャラー2012年

0

同様の問題が発生し、変数の型をチェックするようにイベントハンドラーを変更しました。タイプは、ページが最初にロードされたときではなく、ユーザーが値を選択した後にのみ設定されます。

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

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


0

これを使って:

this.permissionChanged = function (obj, event) {

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

    } 
}

0

これを試してください:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 

2番目のパラメータはインデックスではありませんでした。私にとってはイベントでした。
sairfan

@sairfanは関数にいくつかのパラメーターを追加し、デバッガーを使用してイベントを取得するパラメーターを見つけます
Chanaka Sampath

-1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.