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のいずれかを渡すだけです)。validatorMyViewModelisValidating
サーバー側の検証が失敗したときに、のブレークポイント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__callBacktruefalse - 上記のステップ3、4、および5が繰り返されます。
- observableの値は変更されていないため、サーバーは別の無効な応答を返し、上記のステップ6、7、8、および9をトリガーします。
- 卓球の試合があります。
したがって、問題は、サブスクリプションハンドラーが、ユーザー入力値だけでなく、ネストされたオブザーバブルko.validation.validateObservableへの変更もリッスンしていることのようです。__valid__これはバグですか、それとも私は何か間違ったことをしていますか?
二次的な質問
ko.validation上記のソースから、非同期バリデーターを使用したユーザー入力値は、サーバーが検証している間は有効として扱われることがわかります。nestedModel2.isValid()このため、「真実」を求めることは信頼できません。代わりに、isValidatingフックを使用して非同期バリデーターへのサブスクリプションを作成し、値を通知した後にのみこれらの決定を行う必要があるようですfalse。これは仕様によるものですか?ライブラリの他の部分と比較すると、非同期バリデーターにはisValidatingサブスクライブする必要がなく、真実を伝えるために信頼できるため、これは最も直感に反しているように見えます。.isValid()これも設計によるものですか、それとも私はここでも何か間違ったことをしていますか?