Javascript 淘汰验证多个自定义异步规则
我有一个域属性,我想验证两件事Javascript 淘汰验证多个自定义异步规则,javascript,validation,knockout.js,knockout-validation,Javascript,Validation,Knockout.js,Knockout Validation,我有一个域属性,我想验证两件事 URL存在(可访问) URL存在于我的本地数据库中 为了检查这些内容,我创建了异步验证规则,并在我的属性上应用了它们 发生的情况是,每次来自其中一个规则的响应出现得更早,并且它将isValidating属性设置为false,我希望该属性为true,直到来自第二个规则的响应出现 自定义规则: export function enableCustomValidators() { (ko.validation.rules as any)["urlValidat
isValidating
属性设置为false
,我希望该属性为true
,直到来自第二个规则的响应出现
export function enableCustomValidators() {
(ko.validation.rules as any)["urlValidationServicePath"] = {
async: true,
validator: function (url: string, baseUrl: string, callback: any) {
getRequest(url, baseUrl, callback, "true");
},
message: 'You must enter a reachable domain.',
},
(ko.validation.rules as any)["customerValidationServicePath"] = {
async: true,
validator: function (url: string, baseUrl: string, callback: any) {
getRequest(url, baseUrl, callback, "false");
},
message: "This url already exists in our system. Please contact us at hello@ve.com",
}
ko.validation.registerExtenders();
}
function getRequest(url: string, baseUrl: string, callback: any, method: string) {
var restClient = new RestClient();
restClient.downloadString(baseUrl.concat(url), (responseText) => {
method === "true" ? callback(responseText === "true" ? true : false) :
callback(responseText === "true" ? false : true);
});
}
export class CompanySetupVM extends BasePageVM {
public websiteUrl: KnockoutObservable<string> = ko.observable(undefined);
public isValidating: KnockoutObservable<boolean> = ko.observable(false);
public constructor() {
this.websiteUrl.extend({
required: {
params: true,
message: CompanySetupVM.ErrorMessageNullWebsiteUrl
},
urlValidationServicePath: CompanySetupVM.DomainValidationPath,
customerValidationServicePath: CompanySetupVM.CustomerValidationPath
});
this.isValidating = ko.computed(() => this.websiteUrl.isValidating(), this);
}
}
我已经查看了knockout validation()的源代码,很明显不支持两个独立的异步验证器 一旦异步规则开始运行,
isValidating
属性就会设置为true
,一旦该规则完成,就会再次设置为false
。因此,多个异步规则冲突
只有一个解决办法。删除第二个异步验证程序
您可以在客户端或服务器端将两个检查折叠为一个
要在客户端执行此操作,您需要编写一个验证器,它运行两个Ajax请求,并仅在两个请求都返回后调用验证回调
要在服务器端执行此操作,必须先连续运行“可访问”和“在数据库中”检查,然后才能向客户端提供总体响应
我个人更喜欢改变服务器端,因为
它使客户机代码保持整洁和易于管理
每次检查可以节省一次HTTP往返
从语义上讲,URL检查是一件由于多种原因而失败的事情
让服务器发送自定义验证结果和消息很容易
除了纯true
或false
之外,验证插件还可以理解以下格式的响应:
{isValid: false, message: "something is wrong"}
因此,让您的服务器发送带有适当验证结果和错误消息的JSON响应,让您的REST客户端下载JSON而不是文本
然后,您只需将服务器的响应直接传递给验证回调
ko.validation.rules.urlValidationServicePath = {
async: true,
validator: function (url, baseUrl, callback) {
restClient.downloadJSON(baseUrl.concat(url), callback);
},
message: 'The URL you entered is not valid.'
};
此处的消息
仅为默认值。服务器的消息始终优先于验证规则中的设置。是,正如Tomalak指出的,不可能有多个异步验证器。但我在客户端解决了这个问题,而且这个解决方案非常易于管理和灵活。
这里的诀窍是实现不同的异步验证器作为常规的淘汰扩展器,并使用单个异步规则来调用它们。以下是异步规则:
interface HasAsyncValidator {
asyncValidators: Validator[];
}
interface Validator {
name: string,
validator: (params: any) => boolean | PromiseLike<any>,
params: any
}
interface KnockoutObservable<T> extends HasAsyncValidator {}
ko.validation.rules["validateAsync"] = {
validator: async (value: any, paramsAccessor: () => HasAsyncValidator, callback: (result: boolean | ValidationResult) => void) => {
const params = paramsAccessor();
if (!params || !params.asyncValidators) {
callback(true);
return;
}
try {
const results = await Promise.all(params.asyncValidators.map(v => v.validator(v.params)));
const invalidResult = results.find(r => r.isValid === false);
callback(!!invalidResult ? invalidResult : true);
} catch (error) {
callback(false);
throw error;
}
},
message: 'default message',
async: true
}
注意,我们应该将值访问器传递给validateAsync,而不是值本身。这是必需的,这样异步规则就不会错过稍后可以添加的验证器。您需要显示代码。没有人能通过集中精力来指出你的错误。我已经添加了代码
interface HasAsyncValidator {
asyncValidators: Validator[];
}
interface Validator {
name: string,
validator: (params: any) => boolean | PromiseLike<any>,
params: any
}
interface KnockoutObservable<T> extends HasAsyncValidator {}
ko.validation.rules["validateAsync"] = {
validator: async (value: any, paramsAccessor: () => HasAsyncValidator, callback: (result: boolean | ValidationResult) => void) => {
const params = paramsAccessor();
if (!params || !params.asyncValidators) {
callback(true);
return;
}
try {
const results = await Promise.all(params.asyncValidators.map(v => v.validator(v.params)));
const invalidResult = results.find(r => r.isValid === false);
callback(!!invalidResult ? invalidResult : true);
} catch (error) {
callback(false);
throw error;
}
},
message: 'default message',
async: true
}
ko.extenders["validationRule"] = (target: any, option: any) => {
const validatorObj: Validator = {
name: "validationRule",
params: option,
validator: async (): Promise<boolean | ValidationResult> => {
const unwrappedValue = ko.unwrap(target);
const result = await callServer();
return {
isValid: result.isValid,
message: result.message
};
}
}
addOrUpdateAsyncValidator(target, validatorObj);
};
function addOrUpdateAsyncValidator(target: HasAsyncValidator, validatorObj: Validator) {
target.asyncValidators = target.asyncValidators || [];
const existingRule = target.asyncValidators.find(v => v.name == validatorObj.name);
!!existingRule
? existingRule!.params = validatorObj.params
: target.asyncValidators.push(validatorObj);
}
let value = ko.observable();
value.extend({ validationRule: true, validateAsync: () => value });