我刚刚开始创建表单验证器,遇到一个令人费解的问题。如果我要验证一个简单字段(例如密码),则可以仅在客户端执行此操作(不是说我不会在服务器上对其进行验证)。我的意思是,我可以在客户端计算机上进行验证,而无需任何外部检查。
如果我要验证更复杂的字段(用户名),则需要进行外部检查。例如,在注册表格中,我要验证用户名(确保用户名不存在)。为此,我必须进行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了,我想我已经掌握了它。我已经使其工作到一定程度,所以我仍然需要一些帮助。
我要尝试做的图形描述如下:
我创建了一个异步循环函数,将用于循环这些字段:
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
以及validate函数的当前状态:
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存储库)
答案 0 :(得分:0)
这里有一个可能的实现承诺。这是一个简化的示例,仅专注于管理一系列错误(其中一个错误是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
将字符串视为已解析的承诺。
由于remoteErrorCheck
已经有一个.then
并且已经执行了一个,并且由于它返回了一个承诺,所以Promise of Promise
被压缩为一个承诺,这就是为什么您可以访问{错误数组中的{1}}字符串
这需要适应您的情况(对象而不是字符串,error or ok
内部的验证函数应该从外部传递,...),但这应该是一个很好的起点
答案 1 :(得分:0)
我设法解决了这个问题。我决定不使用自定义的Ajax函数/ lib,而是使用fetch
。
单独使用(不等待)是行不通的,所以我不得不添加它。现在就可以了。
fv.addField("login_name", {
type : "text",
minLength : 4,
maxLength : 32,
required : true,
ajax : async function (fv, field, fieldName) {
await fetch("http://localhost/surec/scripts/user_check.php?field=login_name&value=" + field.elem.value)
.then(function (response) {
return response.json();
}).then(function (response) {
if (!response.error && response.exists) {
fv.addError(fieldName, "This username is taken.");
}
});
},
});
尽管目前可以,但是我不确定这将来是否会引起问题。如果可以,我将更新此质量检查。