淘汰赛验证多个自定义异步规则
Posted
技术标签:
【中文标题】淘汰赛验证多个自定义异步规则【英文标题】:Knockout-Validation multiple custom async rules 【发布时间】:2017-12-02 03:31:44 【问题描述】:我有一个域属性,我想验证两件事;
-
网址存在(可访问)
URL 存在于我的本地数据库中。
为了检查这些内容,我使用 https://github.com/Knockout-Contrib/Knockout-Validation 创建了异步验证规则,并将它们都应用到我的属性上。
发生的情况是,每次来自某个规则的响应较早时,它将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);
在cshtml中:
data-bind="text: currentPage().nextButtonText, css: 'button-overlay': currentPage().isValidating(), 'button': !currentPage().isValidating(), click: nextAction"
【问题讨论】:
您需要出示您的代码。没有人可以仅仅通过非常集中注意力来指出你的错误。 我已添加代码 【参考方案1】:我查看了淘汰验证的源代码 (here),很明显不支持两个独立的异步验证器。
只要异步规则开始运行,isValidating
属性就会设置为 true
,并在该规则完成后再次设置为 false
。因此,多个异步规则发生冲突。
只有一种解决方案。删除第二个异步验证器。
您可以在客户端或服务器端将这两个检查合并为一个。
要在客户端执行此操作,您需要编写一个验证器来运行两个 Ajax 请求并仅在它们都返回后才调用验证 callback
。
要在服务器端执行此操作,您必须连续运行“可访问”和“在数据库中”检查,然后才能向客户端提供整体响应。
我个人更喜欢改变服务器端,因为
-
它使客户端代码保持整洁和易于管理
每次检查可节省一次 HTTP 往返
从语义上讲,URL 检查是一件事失败的原因不止一个
很容易让服务器发送自定义验证结果和-message
除了普通的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.'
;
这里message
只是一个默认值。服务器的message
始终优先于验证规则中的设置。
【讨论】:
【参考方案2】:是的,正如 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
如您所见,我们使用 asyncValidators 属性扩展了 observable,该属性保留了所有已注册的验证器。规则剩下的就是调用验证器(如果有的话),然后将结果传递给敲除验证回调。 这是验证器作为常规扩展器的示例:
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);
请注意,每个验证器都必须将自己注册到 observable 上的 asyncValidators 属性。
这个解决方案的使用非常简单:
let value = ko.observable();
value.extend( validationRule: true, validateAsync: () => value );
请注意,我们应该将 value 访问器传递给 validateAsync 而不是 value 本身。这是必需的,因此异步规则不会错过以后可以添加的验证器。
【讨论】:
以上是关于淘汰赛验证多个自定义异步规则的主要内容,如果未能解决你的问题,请参考以下文章
淘汰赛自定义验证:如何检查 observable 是不是等于特定值?