我真的很喜歡Eric Barnard's knockout validation lib與observables集成,允許分組,&提供自定義驗證器可插件(包括動態驗證器)。有幾個地方可能更具用戶體驗靈活性/友好性,但總體來說它有相當充分的文檔記錄... except, imo, when it comes to async validators。Knockout驗證異步驗證程序:這是一個錯誤還是我做錯了什麼?
今天我做了幾個小時的摔跤,然後再做搜索,並且landing on this。我認爲我有與原作者相同的問題/問題,但同意不清楚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同時具有同步(必需)和異步。異步調用服務器調用來驗證輸入。當服務器響應時,參數callback
用於告知ko.validation
用戶輸入是好還是不好。 如果在指定的行上放置斷點並使用已知的無效值觸發驗證,則最終會出現無限循環,其中ajax success
函數會導致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;
}
此函數位於附加到用戶輸入observable的訂閱鏈中,以便在其值更改時驗證新值。該算法循環連接到輸入的每個驗證程序,並根據驗證程序是否是異步執行單獨的函數。如果同步驗證失敗,則循環被打破,整個validateObservable
函數退出。如果所有同步驗證器均通過,則會執行最後3行,基本上告知ko.validation
該輸入有效。庫中的__valid__
功能如下:
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
兩件事情,從這個帶走:__valid__
是可觀察到的,並且它的validateAsync
函數退出後設置爲true
。現在,讓我們來看看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);
}
需要注意的是ko.validation.validateObservable
設置__valid__
可觀察到真實,退出前僅此函數的第一個和最後一個行執行是非常重要的。 函數是作爲MyViewModel
中聲明的異步validator
函數的第3個參數傳遞的函數。然而在此之前,將調用isValidating
observable的訂戶來通知異步驗證已開始。當服務器調用完成時,調用回調(在這種情況下只傳遞true或false)。
現在,這裏就是爲什麼在MyViewModel
的斷點時,服務器端驗證失敗,也引起無限乒乓循環:在callBack
功能上面,請注意驗證失敗時__valid__
觀察到的設置爲false。以下是發生了什麼情況:
- 無效的用戶輸入更改了可觀察的
nestedModel2.prop2
。 - 通過訂閱此更改通知
ko.validation.validateObservable
。 - 調用
validateAsync
函數。 - 調用自定義異步驗證程序,該程序向服務器提交異步
$.ajax
調用並退出。 ko.validation.validateObservable
將__valid__
設置爲可觀察到true
並且退出。- 服務器返回無效響應,執行
callBack(false)
。 callBack
函數集__valid__
至false
。- 將
ko.validation.validateObservable
通知__valid__
可觀察到的變化(callBack
將其從true
更改爲false
)這基本上重複上面的步驟2。 - 重複上述步驟3,4和5。
- 由於observable的值沒有改變,服務器返回另一個無效響應,觸發上面的步驟6,7,8,&9。
- 我們有自己的乒乓球比賽。
所以好像問題是ko.validation.validateObservable
訂閱處理器是聽不只是用戶的輸入值的變化,也改變了其嵌套__valid__
觀察到。這是一個錯誤,還是我做錯了什麼?
一種二次問題
可以從ko.validation
源上面異步驗證該用戶輸入值看到在服務器驗證它被視爲有效。因此,致電nestedModel2.isValid()
不能依靠「真相」。相反,它看起來像我們必須使用isValidating
鉤子來創建異步驗證器的訂閱,並且只在他們通知值false
後才做出這些決定。這是設計嗎?與庫的其餘部分相比,這似乎是最直觀的,因爲非異步驗證程序沒有isValidating
訂閱,而可以依賴.isValid()
說實話。這是否也是設計,還是我在這裏做錯了?
你已經爲這個問題付出了很多努力,很抱歉看到你沒有任何迴應。我只是新的KO驗證,所以我不能真正幫助你。 – GONeale
這個問題有一些問題。被問到的實際問題並不清楚,並被埋在外部鏈接中。在意識到這一點之前,我必須閱讀幾段文字。這個問題有太多的背景;我看不到樹林。你似乎在問多個問題。 建議:在JSFiddle中創建問題的最簡單的代碼,在您的問題中鏈接到它,並在第一段中提出一個簡潔的問題。做到這一點,你會更有可能得到一些答案。 –
你可以更新或可能回答你的問題?我看到你已經想出了這個網址的解決方案... https://github.com/ericmbarnard/Knockout-Validation/issues/145。你的答案會幫助很多人,因爲沒有太多有關異步驗證的信息。 – bflemi3