許可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関数を呼び出し、許可リストを取得してから、許可項目ごとにイベントを発生させます。これは本当に私が望んでいる動作ではありません。上げる必要がありますユーザーが実際に選択リストでアクセス許可の新しい値を選択した場合のみ、どうすればそれができますか?
実際にはイベントがユーザーまたはプログラムによってトリガーされたかどうかを確認したいであり、初期化中にイベントがトリガーされることは明らかです。
subscription
を追加するノックアウトアプローチは、ほとんどの場合、モデルのほとんどがこのように実装されるため、すべての場合に役立つわけではありません。
(actual KO initilization)
(logical init like load JSON , get data etc)
キャプチャする実際のステップは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
}
}
これは推測に過ぎませんが、level
が数字であるために起こっていると思います。その場合、value
バインディングはchange
イベントをトリガーして、level
をストリング値で更新します。したがって、level
が最初の文字列であることを確認することで、これを修正できます。
さらに、これを行うためのより「ノックアウト」な方法は、イベントハンドラーを使用せず、オブザーバブルとサブスクリプションを使用することです。 level
を監視可能にしてから、それにサブスクリプションを追加します。これは、level
が変更されるたびに実行されます。
この奇妙な動作に役立つ解決策を次に示します。変更イベントを手動でトリガーするボタンを配置するよりも良い解決策を見つけることができませんでした。
編集:このようなカスタムバインディングが役立つかもしれません:
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
私はこのカスタムバインディングを使用します(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>
プリミティブ値の代わりにオブザーバブルを使用する場合、selectは初期バインディングで変更イベントを発生させません。オブザーバブルに直接サブスクライブするのではなく、変更イベントにバインドし続けることができます。
簡単なフラグを使用して、迅速かつ汚い:
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()が既に返されたときに最後の行がまだ保留されている可能性があります。
Knockoutを使用して作業している場合、観察可能な機能ノックアウトの主要な機能を使用します。ko.computed()
メソッドを使用し、その関数内でajax呼び出しを実行してトリガーします。
同様の問題があり、変数の型を確認するためにイベントハンドラを変更しました。タイプは、ページが最初にロードされたときではなく、ユーザーが値を選択した後にのみ設定されます。
self.permissionChanged = function (l) {
if (typeof l != 'undefined') {
...
}
}
これは私にとってはうまくいくようです。
これを使って:
this.permissionChanged = function (obj, event) {
if (event.type != "load") {
}
}