Eric Barnardのノックアウト検証ライブラリがオブザーバブルと統合され、グループ化が可能になり、カスタムバリデーターのプラグ可能性(オンザフライバリデーターを含む)を提供する方法が本当に気に入っています。より柔軟で使いやすいUXになる可能性のある場所がいくつかありますが、全体としてはかなり十分に文書化されています...非同期バリデーターに関しては、imoを除きます。
私はこれに数時間取り組んだ後、検索を行ってこれに着陸しました。私は元の作者と同じ問題/質問があると思いますが、duxaが何を求めているのかが正確に明確ではなかったことに同意します。もっと注目させたいので、ここでもお願いします。
function MyViewModel() {
var self = this;
self.nestedModel1.prop1 = ko.observable().extend({
required: { message: 'Model1 Prop1 is required.' },
maxLength: {
params: 140,
message: '{0} characters max please.'
}
});
self.nestedModel2.prop2 = ko.observable().extend({
required: { message: 'Model2 Prop2 is required' },
validation: {
async: true,
validator: function(val, opts, callback) {
$.ajax({ // BREAKPOINT #1
url: '/validate-remote',
type: 'POST',
data: { ...some data... }
})
.success(function(response) {
if (response == true) callback(true); // BREAKPOINT #2
else callback(false);
});
},
message: 'Sorry, server says no :('
}
});
}
ko.validation.group(self.nestedModel1);
ko.validation.group(self.nestedModel2);
上記のコードに関する注意事項:ネストされたモデルごとに1つずつ、2つの個別の検証グループがあります。ネストされたモデル#1には非同期バリデーターがなく、ネストされたモデル#2には同期(必須)と非同期の両方があります。asyncは、サーバー呼び出しを呼び出して入力を検証します。サーバーが応答すると、callback
引数はko.validation
ユーザー入力が良いか悪いかを判断するために使用されます。示された行にブレークポイントを設定し、既知の無効な値を使用して検証をトリガーすると、ajaxsuccess
関数によってvalidator
関数が再度呼び出される無限ループが発生します。ko.validation
ソースをクラックして開いて、何が起こっているのかを確認しました。
ko.validation.validateObservable = function(observable) {
// set up variables & check for conditions (omitted for brevity)
// loop over validators attached to the observable
for (; i < len; i++) {
if (rule['async'] || ctx['async']) {
//run async validation
validateAsync();
} else {
//run normal sync validation
if (!validateSync(observable, rule, ctx)) {
return false; //break out of the loop
}
}
}
//finally if we got this far, make the observable valid again!
observable.error = null;
observable.__valid__(true);
return true;
}
この関数は、監視可能なユーザー入力に接続されたサブスクリプションチェーン内にあるため、値が変更されたときに新しい値が検証されます。アルゴリズムは、入力に接続された各バリデーターをループし、バリデーターが非同期であるかどうかに応じて個別の関数を実行します。同期検証が失敗すると、ループが中断され、validateObservable
関数全体が終了します。すべての同期バリデーターが合格すると、最後の3行が実行され、基本的にko.validation
この入力が有効であることを示します。ライブラリ内の__valid__
関数は次のようになります。
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
これから取り除くべき2つのこと:__valid__
は監視可能であり、関数が終了しtrue
た後に設定されます。validateAsync
それでは、見てみましょうvalidateAsync
:
function validateAsync(observable, rule, ctx) {
observable.isValidating(true);
var callBack = function (valObj) {
var isValid = false,
msg = '';
if (!observable.__valid__()) {
// omitted for brevity, __valid__ is true in this scneario
}
//we were handed back a complex object
if (valObj['message']) {
isValid = valObj.isValid;
msg = valObj.message;
} else {
isValid = valObj;
}
if (!isValid) {
//not valid, so format the error message...
observable.error = ko.validation.formatMessage(...);
observable.__valid__(isValid);
}
// tell it that we're done
observable.isValidating(false);
};
//fire the validator and hand it the callback
rule.validator(observable(), ctx.params || true, callBack);
}
observableをtrueにko.validation.validateObservable
設定して終了する前に、この関数の最初と最後の行のみが実行されることに注意することが重要です。__valid__
この関数は、で宣言されcallBack
た非同期関数に3番目のパラメーターとして渡されるものです。ただし、これが発生する前に、observableのサブスクライバーが呼び出され、非同期検証が開始されたことを通知します。サーバー呼び出しが完了すると、コールバックが呼び出されます(この場合、trueまたはfalseのいずれかを渡すだけです)。validator
MyViewModel
isValidating
サーバー側の検証が失敗したときに、のブレークポイントMyViewModel
が無限のピンポンループを引き起こしている理由は次のとおりです。上記の関数で、検証が失敗したときにobservableがfalseに設定されていることcallBack
に注目してください。__valid__
何が起こるかです:
- 無効なユーザー入力により、
nestedModel2.prop2
オブザーバブルが変更されます。 - は
ko.validation.validateObservable
、この変更のサブスクリプションを介して通知されます。 validateAsync
関数が呼び出されます。- カスタム非同期バリデーターが呼び出され
$.ajax
、サーバーに非同期呼び出しが送信されて終了します。 - は
ko.validation.validateObservable
、__valid__
observableをに設定しtrue
て終了します。 - サーバーは無効な応答を返し、
callBack(false)
実行されます。 callBack
関数はに設定さ__valid__
れfalse
ます。- は、オブザーバブルへの変更が通知されます(から
ko.validation.validateObservable
に変更されました)。これは、基本的に上記の手順2を繰り返します。__valid__
callBack
true
false
- 上記のステップ3、4、および5が繰り返されます。
- observableの値は変更されていないため、サーバーは別の無効な応答を返し、上記のステップ6、7、8、および9をトリガーします。
- 卓球の試合があります。
したがって、問題は、サブスクリプションハンドラーが、ユーザー入力値だけでなく、ネストされたオブザーバブルko.validation.validateObservable
への変更もリッスンしていることのようです。__valid__
これはバグですか、それとも私は何か間違ったことをしていますか?
二次的な質問
ko.validation
上記のソースから、非同期バリデーターを使用したユーザー入力値は、サーバーが検証している間は有効として扱われることがわかります。nestedModel2.isValid()
このため、「真実」を求めることは信頼できません。代わりに、isValidating
フックを使用して非同期バリデーターへのサブスクリプションを作成し、値を通知した後にのみこれらの決定を行う必要があるようですfalse
。これは仕様によるものですか?ライブラリの他の部分と比較すると、非同期バリデーターにはisValidating
サブスクライブする必要がなく、真実を伝えるために信頼できるため、これは最も直感に反しているように見えます。.isValid()
これも設計によるものですか、それとも私はここでも何か間違ったことをしていますか?