javascript - 在提交表单之前使用 ajax 验证字段

标签 javascript ajax forms validation promise

我刚开始创建一个表单验证器,遇到了一个令人费解的问题。如果我要验证一个简单的字段(比如密码),我可以单独在客户端执行此操作(并不是说我不会在服务器上验证它)。我的意思是我可以在客户端机器上进行验证而无需任何外部检查。

如果我要验证一个更复杂的字段(用户名),这需要外部检查。例如,在注册表中,我想验证用户名(确保它不存在)。为此,我必须进行 ajax 调用。这让事情变得有点复杂。检查下面的代码。

FormValidator.prototype.validate = function validateForm() {
    this.errors = {};
    for (var fieldName in this.fields) {
        var field = this.fields[fieldName];

        if (field.hasOwnProperty("required") && field.required && field.elem.value.length == 0) {
            this.addError(fieldName, "This field is required.");
            break;
        }

        if (field.hasOwnProperty("minLength") && field.elem.value.length < field.minLength) {
            this.addError(fieldName, "Input length should not be less than  " + field.minLength + " characters.");
            break;
        }

        if (field.hasOwnProperty("maxLength") && field.elem.value.length > field.maxLength) {
            this.addError(fieldName, "Input length should not be greater than" + field.maxLength + " characters.");
            break;
        }

        if (field.hasOwnProperty("ajax")) {
            // FormValidator can't possibly know what the call will return, so we can't add the error here
            // it has to be done manually
            field.ajax(this, field, fieldName);
        }
    }
    if (this.errors.length != 0) {
        // warn the user
        console.log(this.errors);
    }
};

var fv = new FormValidator(document.forms[0]);

fv.addField("login_name", {
    type      : "text",
    minLength : 4,
    maxLength : 32,
    required  : true,
    ajax      : function (fv, field, fieldName) {
        ajax("http://localhost/surec/scripts/user_check.php?field=login_name&value=" + field.elem.value, {
            success : function () {
                var response = JSON.parse(this.response);
                // manually adding the error
                if (!response.error && response.exists) {
                    fv.addError(fieldName, "This username is taken.");
                }
            },
            // async: false,
        });
    },
});

// called on form submit
// fv.validate();

调用 fv.validate() 的那一刻(假设​​用户输入了一个被占用的用户名),validate() 不会做任何事情来警告用户,因为ajax 调用是异步的。错误检查完成后 if (this.errors.length != 0) {,错误将为空。它会在 ajax 调用完成时填充,然后为时已晚。

要解决此问题,我可以使 ajax 调用同步。这解决了问题,但我不确定是否使用同步调用。这种方法是否有效,或者是否有我可以采用的替代方法?


更新:我已经开始研究 Promise,我想我已经掌握了它的窍门。我已经让它工作到一定程度,所以我仍然需要一些帮助。

我正在尝试做的事情的图形描述是这样的:

enter image description here

我已经创建了一个异步循环函数,我将使用它来循环字段:

async function asyncForEach(array, callback) {
    for (let index = 0; index < array.length; index++) {
        await callback(array[index], index, array);
    }
}

以及验证函数的当前状态:

FormValidator.prototype.validate = async function validateForm() {
    this.errors = {};
    var self = this;

    await asyncForEach(self.fields.keys(), async function (fieldName) {
        var field = self.fields[fieldName];

        if (field.hasOwnProperty("required") && field.required && field.elem.value.length == 0) {
            self.addError(fieldName, "This field is required.");
            // break;
        }

        if (field.hasOwnProperty("minLength") && field.elem.value.length < field.minLength) {
            self.addError(fieldName, "Input length should not be less than " + field.minLength + " characters.");
            // break;
        }

        if (field.hasOwnProperty("maxLength") && field.elem.value.length > field.maxLength) {
            self.addError(fieldName, "Input length should not be greater than " + field.maxLength + " characters.");
            // break;
        }

        if (field.hasOwnProperty("ajax")) {
            // FormValidator can't possibly know what the call will return, so we can't add the error here
            // it has to be done manually
            await field.ajax(self, field, fieldName);
        }

    });

    if (self.errors.length() != 0) {
        // warn the user
        console.log("errors: ", self.errors);
    }
};

这似乎有效(在测试用例中)。见下文。我没有使用 ajax 函数,而是伪造了它(使用 1 秒延迟)。 console.log("errors: ", self.errors); 中的 fv.validate() 在外部检查(假 ajax)完成后运行(延迟 1 秒)。

var fv = new FormValidator(document.forms[0]);

fv.addField("login_name", {
    type      : "text",
    minLength : 4,
    maxLength : 32,
    required  : true,
    ajax : async function (fv, field, fieldName) {
        // assume delay is ajax
        await delay(1000);
        // and on success (and login_name collision) we add an error
        fv.addError(fieldName, "This username is taken.");
    },
});

var delay = (ms) => new Promise(r => setTimeout(r, ms));

fv.validate();

现在,我需要重写外部检查方法 (ajax) 并使其与这段代码一起工作。我已经尝试了一些组合(可能是废话,使用试错),但无法使其工作。我应该如何进行?

FormValidator.js (GitHub repo )

最佳答案

这是一种可能的 promise 实现。这是一个简化的示例,只关注管理一组错误,其中一个是 ajax 调用的结果

// errors is an array of promises
const errors = ["Too long"] 

const remoteErrorCheck = $.ajax({type:"GET", url:'https://api.stackexchange.com/2.2/info?site=stackoverflow'}).then(function(data){ 
    // here you have access to the result of ajax request, inside data
    return Promise.resolve("error or ok")
    // you want to return a promise to put in the array of promises
})

// now put the promise from the ajax request into the array of promises
errors.push(remoteErrorCheck)

// when all the promises are resolved, then do something with the results
Promise.all(errors).then((a)=>{
  console.log(a)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

请注意,Promise.all 在执行之前等待 ajax 完成。 Promise.all 将字符串视为已解决的 promise 。

因为 remoteErrorCheck 已经有一个 .then 也被执行并且因为它返回一个 promise ,Promise of Promise 被压缩成只是一个 promise ,这就是为什么您可以访问错误数组中的 error 或 ok 字符串

这需要根据您的情况进行调整(对象而不是字符串,then 中的验证函数应该从外部传递,...)但它应该是一个很好的起点

关于javascript - 在提交表单之前使用 ajax 验证字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50970095/

相关文章:

asp.net-mvc - Controller 和操作方法之间丢失的 session 变量

javascript - Ajax文件上传,想传递一个int,但我不知道如何

java - 从 ajax 成功调用传递值

Django 表单验证,将经过身份验证的用户作为字段

javascript - 在 HTML 表单中包含动态生成的引用

javascript - 当表单具有 "enter"输入时,从输入(文本)字段中命中 "submit"

javascript - ES6/Typescript OnClick 向单击按钮内的所有 TD 添加一个类,第一个除外(无 jQuery)

javascript - Javascript 中的引用数组与值数组

javascript - 将 React 组件集成/注入(inject)到静态(已加载)HTML 中

javascript - 理解石头、剪刀、布中的嵌套 if 语句 Codecademy Javascript 练习