knockout.js 敲除-验证多个自定义异步规则

uqcuzwp8  于 2022-11-10  发布在  其他
关注(0)|答案(2)|浏览(211)

我有一个域属性,我想验证两件事:

  1. URL存在(可访问)
  2. URL存在于我的本地数据库中。
    为了检查这些东西,我使用https://github.com/Knockout-Contrib/Knockout-Validation创建了异步验证规则,并在我的属性上应用了这两个规则。
    发生的情况是,每次来自其中一个规则的响应提前到来,它将isValidating属性设置为false,我希望此属性为true,直到来自第二个规则的响应到来。
    1.自订规则:
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);
});
}

1.规则的使用:

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);   
    }
}

1.在cshtml中:

data-bind="text: currentPage().nextButtonText, css: {'button-overlay': currentPage().isValidating(), 'button': !currentPage().isValidating()}, click: nextAction"
bttbmeg0

bttbmeg01#

我看过knockout validation(here)的源代码,很明显不支持两个独立的异步验证器。
一旦异步规则开始执行,isValidating属性就会设定为true,而一旦该规则完成,就会再次设定为false。因此,多个异步规则会恩怨。
只有一个解决方案。请删除第二个异步验证程序。
您可以在客户端或服务器端将这两个检查收合为一个。
要在客户端执行此操作,需要编写一个验证器,运行两个 AJAX 请求,并仅在两个请求都返回后才调用验证callback
要在服务器端执行此操作,您必须在向客户端提供整体响应之前连续运行“is reachable”和“is in DB”检查。
就我个人而言,我更喜欢更改服务器端,因为
1.它使客户端代码保持整洁和可管理,
1.每次检查节省一次HTTP往返
1.从语义上讲,URL检查是“一件事”,失败的原因不止一个
1.让服务器发送自定义的验证结果和消息是很容易的
除了普通的truefalse之外,验证插件还可以识别以下格式的响应:

{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总是优先于验证规则中的设置。

w8ntj3qf

w8ntj3qf2#

是的,正如Tomalak所指出的,不可能有多个异步验证器。但是我在客户端解决了这个问题,而且解决方案是相当可管理和灵活的。这里的技巧是实现不同的异步验证器作为常规的knockout扩展器,并有一个异步规则来调用它们。下面是异步规则:

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 });

注意,我们应该将值访问器而不是值本身传递给validateAsync,这样异步规则就不会错过稍后添加的验证器。

相关问题