Me gusta mucho cómo Eric Barnard's knockout validation lib se integra con observables, permite la agrupación, & ofrece pluggabilidad validador personalizado (incluidos los validadores sobre la marcha). Hay un par de lugares donde podría ser más flexible/amigable con UX, pero en general está razonablemente bien documentado ... except, imo, when it comes to async validators.Validadores asincrónicos de validación no autorizada: ¿Esto es un error o estoy haciendo algo mal?
Luché con esto por unas pocas horas hoy antes de hacer una búsqueda y landing on this. I think Tengo los mismos problemas/preguntas que el autor original, pero estoy de acuerdo en que no estaba claro exactamente qué estaba pidiendo duxa. Quiero llamar más la atención de la pregunta, así que también estoy preguntando aquí.
function MyViewModel() {
var self = this;
self.nestedModel1.prop1 = ko.observable().extend({
required: { message: 'Model1 Prop1 is required.' },
maxLength: {
params: 140,
message: '{0} characters max please.'
}
});
self.nestedModel2.prop2 = ko.observable().extend({
required: { message: 'Model2 Prop2 is required' },
validation: {
async: true,
validator: function(val, opts, callback) {
$.ajax({ // BREAKPOINT #1
url: '/validate-remote',
type: 'POST',
data: { ...some data... }
})
.success(function(response) {
if (response == true) callback(true); // BREAKPOINT #2
else callback(false);
});
},
message: 'Sorry, server says no :('
}
});
}
ko.validation.group(self.nestedModel1);
ko.validation.group(self.nestedModel2);
Un par de notas sobre el código anterior: hay 2 grupos de validación separados, uno para cada modelo anidado. El modelo anidado n. ° 1 no tiene validadores asincrónicos, y el modelo anidado n. ° 2 tiene una sincronización (requerida) y una asincrónica. La función async invoca una llamada al servidor para validar las entradas. Cuando el servidor responde, el argumento callback
se usa para indicar ko.validation
si la entrada del usuario es buena o mala. Si coloca puntos de interrupción en las líneas indicadas y desencadena la validación utilizando un valor inválido conocido, termina con un bucle infinito donde la función ajax success
hace que se vuelva a llamar a la función validator
. Rompí la fuente ko.validation
para ver qué estaba pasando.
ko.validation.validateObservable = function(observable) {
// set up variables & check for conditions (omitted for brevity)
// loop over validators attached to the observable
for (; i < len; i++) {
if (rule['async'] || ctx['async']) {
//run async validation
validateAsync();
} else {
//run normal sync validation
if (!validateSync(observable, rule, ctx)) {
return false; //break out of the loop
}
}
}
//finally if we got this far, make the observable valid again!
observable.error = null;
observable.__valid__(true);
return true;
}
Esta función es en una cadena de suscripción unido a la entrada del usuario observable de manera que cuando sus cambios de valor, se validarán el nuevo valor. El algoritmo realiza un ciclo sobre cada validador conectado a la entrada y ejecuta funciones separadas dependiendo de si el validador es asincrónico o no. Si la validación de sincronización falla, el ciclo se interrumpe y la función completa validateObservable
se cierra. Si se aprueban todos los validadores de sincronización, se ejecutan las últimas 3 líneas, indicando esencialmente ko.validation
que esta entrada es válida. La función __valid__
en la biblioteca se ve así:
//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);
Dos cosas se lleve de esto: __valid__
es un observable, y que se establece en true
después de los validateAsync
sale de la función. Ahora vamos a echar un vistazo a validateAsync
:
function validateAsync(observable, rule, ctx) {
observable.isValidating(true);
var callBack = function (valObj) {
var isValid = false,
msg = '';
if (!observable.__valid__()) {
// omitted for brevity, __valid__ is true in this scneario
}
//we were handed back a complex object
if (valObj['message']) {
isValid = valObj.isValid;
msg = valObj.message;
} else {
isValid = valObj;
}
if (!isValid) {
//not valid, so format the error message...
observable.error = ko.validation.formatMessage(...);
observable.__valid__(isValid);
}
// tell it that we're done
observable.isValidating(false);
};
//fire the validator and hand it the callback
rule.validator(observable(), ctx.params || true, callBack);
}
Es importante tener en cuenta que sólo la primera y la última línea de esta función se ejecutan antes ko.validation.validateObservable
establece el __valid__
observables en true y salidas. La función callBack
es lo que se pasa como el tercer parámetro de la función async validator
declarada en MyViewModel
. Sin embargo, antes de que esto ocurra, se invocan los suscriptores de un observador isValidating
para notificar que la validación asíncrona ha comenzado. Cuando se completa la llamada al servidor, se invoca la devolución de llamada (en este caso, solo pasa verdadero o falso).
Ahora aquí es la razón por los puntos de interrupción en MyViewModel
están causando un bucle infinito cuando ping pong validación del lado del servidor falla: En la función callBack
anterior, observe cómo el __valid__
observables se establece en falso cuando falla la validación. Esto es lo que sucede:
- La entrada de usuario no válida cambia el
nestedModel2.prop2
observable. - El
ko.validation.validateObservable
se notifica mediante la suscripción de este cambio. - Se invoca la función
validateAsync
. - Se invoca el validador asíncrono personalizado, que envía una llamada asincrónica
$.ajax
al servidor y se cierra. ko.validation.validateObservable
establece__valid__
observable entrue
y sale.- El servidor devuelve una respuesta no válida y se ejecuta
callBack(false)
. - La función
callBack
establece__valid__
enfalse
. - El
ko.validation.validateObservable
se notifica el cambio a la__valid__
observable (callBack
cambiaron detrue
afalse
) Esto se repite esencialmente el paso 2 anterior. - Se repiten los pasos 3, 4 y 5 anteriores.
- Dado que el valor del observable no ha cambiado, el servidor devuelve otra respuesta no válida, activando los pasos 6, 7, 8, & 9 arriba.
- Tenemos nosotros mismos un partido de ping pong.
así que parece que el problema es que el manejador de suscripción ko.validation.validateObservable
está escuchando a los cambios no sólo al valor de entrada del usuario, sino que también cambia a su anidada __valid__
observable. ¿Es esto un error, o estoy haciendo algo mal?
Una cuestión secundaria
Se puede ver en las fuentes anteriores ko.validation
que un valor de entrada del usuario con un validador asíncrono se trata como válida mientras el servidor está validando la misma. Debido a esto, no se puede confiar en llamar al "nestedModel2.isValid()
" por "la verdad". En su lugar, parece que tenemos que usar los ganchos isValidating
para crear suscripciones a los validadores asíncronos, y solo tomar estas decisiones después de que notifiquen un valor de false
. ¿Esto es por diseño? Comparado con el resto de la biblioteca, este parece ser el más intuitivo porque no validadores asincrónicos no tienen un isValidating
para suscribirse, y puede confiar en .isValid()
para decir la verdad. ¿Esto también es por diseño, o estoy haciendo algo mal aquí también?
Ha hecho un gran esfuerzo en esta cuestión, lamento ver que no ha recibido ninguna respuesta. Solo soy nuevo en la validación KO, así que realmente no puedo ayudarte. – GONeale
Hay algunos problemas con esta pregunta. La pregunta real que se hace no es clara y está enterrada en un enlace externo. Tuve que leer varios párrafos de texto antes de darme cuenta de esto. Esta pregunta tiene demasiado contexto; No puedo ver el bosque por los árboles. Y parece que estás haciendo preguntas múltiples. Sugerencia: cree la reproducción más simple del problema en un JSFiddle, enlace a él en su pregunta y formule una pregunta concisa en el primer párrafo. Haga esto y será más probable que obtenga algunas respuestas. –
¿Se puede actualizar o posiblemente responder a su pregunta? Veo que ha encontrado una solución a partir de esta url ... https://github.com/ericmbarnard/Knockout-Validation/issues/145. Su respuesta ayudará a muchas personas ya que no hay mucha información sobre la validación asincrónica. – bflemi3