我相信至少有一个问题是从 .addMethod() 中调用 .valid()。将其包装在 setTimeout 中似乎效果更好。
https://codepen.io/anon/pen/mWqJbo?editors=1011
HTML:
<form id='addressForm'>
<input id='postalcode' type='postalcode' name='postalcode' placeholder='postal code' />
<br/>
<input name='city' required placeholder='city' />
<br/>
</form>
<button id='submit'>submit</button>
<div id='success' style='display:none'>Success</div>
JS:
var myUrl = "https://api.zippopotam.us/us/";
var postalcode = ''
$('input[name=postalcode]').on('change', function() {
$('#success').hide();
$(this).valid();
});
$('#submit').on('click', function() {
$('#success').hide();
if ($('#addressForm').valid()) {
$('#success').show();
}
});
//init jquery validation
$("#addressForm").validate({
messages: {
postalcode: 'Please enter a valid postal code'
},
rules: {
postalcode: {
required: true,
postalcode: true
},
city: {
required: true,
}
}
});
$.validator.addMethod('postalcode', function(value, $elem) {
/* don't re-validate postalcode on submit -
saves time & prevent edits to the city field from being overwritten.
If edits to city shouldn't be allowed, make city field readonly.
Note that many zipcodes map to multiple cities (at least in the US). You may want to present
user with a selectable list in those cases.
*/
if (postalcode && value === postalcode) {
return true;
}
var is_success = false;
var city = ''
$.ajax({
url: myUrl + $('input[name=postalcode]').val(),
data: {
postal_code: value
},
json: true,
async: false,
success: function(response) {
is_success = typeof(response['post code']) !== 'undefined';
city = response.places[0]['place name'];
},
error: function(err) {
//zippopotam api returns 404 if zip is invalid
if (err.status === 404) {
//clear last valid postal code
postalcode = '';
$('input[name=city]').val('');
$('#success').hide();
}
//fixme handle other errors / notify user
console.log('error ' + JSON.stringify(err));
}
});
if (is_success) {
$('input[name=city]').val(city);
//remember last valid postal code
postalcode = $('input[name=postalcode]').val();
//previous error message(s) may not be cleared if .valid() is called from within .addMethod()
setTimeout(function() {
$('input[name="city"]').valid();
}, 1);
}
return is_success;
}, 'Please enter a VALID postal code');
您的代码使用同步 ajax,这通常不是一个好主意。如果你想让它异步,你需要使用jquery validation remote method
但是,如果数据有效,该方法假定 ajax 响应将返回“true”。您需要能够修改后端,以便有一个“有效”端点,如果邮政编码有效,该端点将返回 true。我相信您还需要添加第二个端点来返回相应的城市。
您可以使用自定义实现覆盖它,而不是让您的后端符合 remote 的要求。
这是一个示例 (codepen):
HTML:
<form id='addressForm'>
<input id='postalcode' type='postalcode' name='postalcode' placeholder='postal code'/>
<br/>
<input name='city' required placeholder='city'/>
<br/>
</form>
<button id='submit'>submit</button>
<div id='success' style='display:none'>Success</div>
JS:
//override
$.validator.methods.remote = function(value, element, param, method) {
//zippopotam specific
param.url = myUrl + value;
if (this.optional(element)) {
return "dependency-mismatch";
}
method = typeof method === "string" && method || "remote";
var previous = this.previousValue(element, method),
validator, data, optionDataString;
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
this.settings.messages[element.name][method] = previous.message;
param = typeof param === "string" && {
url: param
} || param;
optionDataString = $.param($.extend({
data: value
}, param.data));
if (previous.old === optionDataString) {
return previous.valid;
}
previous.old = optionDataString;
validator = this;
this.startRequest(element);
data = {};
data[element.name] = value;
$.ajax($.extend(true, {
mode: "abort",
port: "validate" + element.name,
dataType: "json",
data: data,
context: validator.currentForm,
success: function(response) {
//zippopotam specific -> write your own definition of valid
var valid = typeof(response['post code']) !== 'undefined',
errors, message, submitted;
///////////////
validator.settings.messages[element.name][method] = previous.originalMessage;
if (valid) {
//your code here
$('input[name=city]').val(response.places[0]['place name'])
//end your code
submitted = validator.formSubmitted;
validator.resetInternals();
validator.toHide = validator.errorsFor(element);
validator.formSubmitted = submitted;
validator.successList.push(element);
validator.invalid[element.name] = false;
validator.showErrors();
} else {
//your code here if api returns successfully with an error object
//end your code
errors = {};
message = response || validator.defaultMessage(element, {
method: method,
parameters: value
});
errors[element.name] = previous.message = message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
previous.valid = valid;
validator.stopRequest(element, valid);
},
error: function(response) {
//your code here if api fails on bad zip, e.g. zippopotam returns 404
if (response.status === 404) {
$('input[name=city]').val('');
//end your code
errors = {};
message = validator.defaultMessage(element, {
method: method,
parameters: value
});
errors[element.name] = previous.message = message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
previous.valid = false;
validator.stopRequest(element, false);
} else {
console.log('problem with validation server');
}
}
}, param));
return "pending";
};
var myUrl = "https://api.zippopotam.us/us/";
//init jquery validation
$("#addressForm").validate({
messages: {
postalcode: 'Please enter a valid postal code'
},
rules: {
postalcode: {
required: true,
remote: {
url: myUrl,
type: "get",
data: ''
}
},
city: {
required: true,
}
}
});
$('input[name=postalcode]').on('change', function() {
$('#success').hide();
$('input[name=postalcode]').valid()
})
$('#submit').on('click', function() {
if ($("#addressForm").valid()) {
$('#success').show();
} else {
$('#success').hide();
}
})