下部の編集を参照してください。
私の会社には巨大なコード ベースがあり、ノックアウトをより効果的に使用したいと考えています。ただし、クライアント側の検証のすべての側面を処理する検証コードが既に用意されています。jQuery を使用して検証エラー メッセージを表示し、ユーザー入力をサニタイズします。
たとえば、クラス「validate-range」を入力に追加すると、jQuery の変更/フォーカスアウト イベントを使用して変更を追跡し、値が範囲外の場合は、使用して最小/最大値に置き換えます。 $(input).val(). この検証コードはプログラムによってこのように変更を行うため、この種の変更が行われた場合、私のノックアウト ビュー モデルは更新されません。
この検証コードはシステムのいたるところで使用されており、現時点では置き換えることができないため、ノックアウトを使用するには、このコードと一緒に機能するようにする必要があります。これまでに試したことは、検証コードが入力の値を変更するたびにビューモデルを更新するために使用される追加の変更イベントハンドラーを追加するカスタム値バインディングを作成することです。
これは、 foreach バインディング内を除くすべてのケースで驚くほどうまく機能します (これは、テンプレートを使用する場合と同じであり、バインディングを使用すると思います)。監視可能な配列が変更されるたびに foreach 内のすべての入力にカスタム バインディングが再適用されているにもかかわらず、カスタム値バインディングを使用する foreach 内の入力で変更イベント ハンドラーが起動されません。
誰かが以前にこの問題に対処したことがあると思っていました.DOM値を変更する既存のjavascriptコードでノックアウトを機能させなければならず、したがってビューモデルを更新しません. どんな助けでも大歓迎です。
カスタム バインディング、ビュー モデルの作成、および古い検証コードの Javascript コード:
// custom value binding for amounts
ko.bindingHandlers.amountValue = {
init: function (element, valueAccessor) {
var underlyingObservable = valueAccessor(),
interceptor = ko.computed({
read: function () {
var value = underlyingObservable();
return formatAmount(value);
},
write: function (newValue) {
var current = underlyingObservable(),
valueToWrite = parseAmount(newValue);
if (valueToWrite !== current)
underlyingObservable(valueToWrite);
else if (newValue !== current.toString())
underlyingObservable.valueHasMutated();
}
});
// i apply a change event handler when applying the bindings which calls the write function of the interceptor.
// the intention is to have the change handler be called anytime the old validation code changes an input box's value via
// $(input).val("new value"); In the case of the foreach binding, whenever the observable array changes, and the table rows
// are re-rendered, this code does get ran when re-applying the bindings, however the change handler doesn't get called when values are changed.
ko.applyBindingsToNode(element, { value: interceptor, event: { change: function () { interceptor($(element).val()); } } });
}
};
// view model creation
// auto create ko view model from json sent from server
$(function () {
viewModel = ko.mapping.fromJS(jsonModel);
ko.applyBindings(viewModel);
});
// old validation code
$(document).on("focusout", ".validate-range", function () {
var $element = $(this),
val = $element.val(),
min = $element.attr("data-val-range-min"),
max = $element.attr("data-val-range-max");
if (val < min)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(min);
if (val > max)
// my change handler from custom binding doesn't fire after this to update view model
$element.val(max);
// more code to show error message
});
foreach バインディング内でカスタム バインディングを使用する HTML コード:
<table>
<thead>
<tr>
<td>Payment Amount</td>
</tr>
</thead>
<tbody data-bind="foreach: Payments">
<tr>
<td><input type="text" class="validate-range" data-val-range-min="0" data-val-range-max="9999999" data-bind="amountValue: Amount" /></td>
</tr>
</tbody>
</table>
上記の例で、金額テキスト ボックスに「-155」と入力すると、カスタム バインドが実行され、ビュー モデルの金額が -155 に設定されます。次に、古い検証が実行され、$(input).val(0) でテキスト ボックスの値が "0" に再設定されます。私のビューモデルはこの時点では更新されておらず、-155 の値を反映しています。ビュー モデルを 0 に更新するためにカスタム バインディングからの変更イベント ハンドラーが実行されるはずですが、実行されません。
編集:
回答で指摘されているように、 .val() は変更イベントをトリガーしません。私が追加した変更イベント ハンドラーは何もしませんでした。検証コードが foreach バインディングの外部の値を変更したときにビュー モデルが更新されていた理由は、JavaScript コードの別の場所に、blur イベントを使用して手動で変更イベントをトリガーするロジックがあり、それがカスタム バインディングをトリガーしたためです。ビューモデルを実行および更新します。このぼかしイベント ハンドラーは、委任されるのではなく、テキスト ボックスに直接バインドされていたため、ページが最初にレンダリングされたときにそこにあったテキスト ボックスには機能しましたが、foreach バインディングによって動的に挿入されたテキスト ボックスには機能しませんでした。
今のところ、このロジックをドキュメント内のイベントを委任するように変更しただけなので、動的に挿入されたテキスト ボックスが含まれるようになり、問題なく動作しているようです。今後、より良い解決策を考えたいと思います。