javascript - example - Knockout.js parece estar golpeando a mis manejadores de eventos jQuery, qué grosero
django javascript example (6)
En lugar de anular el controlador de base de verificación, solo crea tu propio enlace personalizado para actualizar la UI.
Aquí hay un ejemplo de un modelo y un enlace personalizado para manejar la actualización de la IU:
var Model = function () {
this.checked = ko.observable(false);
};
ko.bindingHandlers.customCheckbox = {
init: function (element) {
// Create the custom checkbox here
$(element).customInput();
},
update: function (element) {
// Update the checkbox UI after the value in the model changes
$(element).trigger(''updateState'');
}
};
Enlazaré el modelo al siguiente HTML:
<input type="checkbox" name="genre" id="check-1" value="action" data-bind="checked: checked, customCheckbox: checked" />
Y realmente eso es todo lo que tendré que hacer.
Aquí hay un ejemplo: http://jsfiddle.net/badsyntax/4Cy3y/
[edit] - Creo que me apresuré a leer tu pregunta y no encontré el quid de lo que estabas preguntando. Dejaré mi respuesta aquí en cualquier caso.
Ok, he estado tratando de desenmarañar este desastre por unas pocas horas y no he llegado a ninguna parte, análogo a un perro persiguiendo su cola. Aquí está la situación.
Estoy usando Knockout.js para mi interfaz de usuario, que funciona muy bien por sí mismo. Sin embargo, estoy tratando de usar un código de terceros que hace que las listas desplegables y las casillas se vean bonitas. En realidad, ni siquiera estoy seguro de si esta es una biblioteca de terceros o simplemente algo que nuestros diseñadores escribieron. Este código oculta la casilla de verificación real y la reemplaza con un falso <span />
que imita una casilla de verificación a través de CSS. El evento de click
del intervalo activa el evento de change
de la casilla de verificación real:
// this code updates the fake UI
this._changeEvent = function() {
self.isChecked = self.$input.is('':checked'');
self._updateHTML(false, true);
jQuery(self).trigger(''change'');
};
// when the user clicks the fake checkbox, we trigger change on the real checkbox
this.$fake.on(''click'', function(e) {
e.preventDefault();
self.$input.click().trigger(''change'');
});
// Bind _changeEvent to the real checkbox
this.$input.change(this._changeEvent);
Esto realmente funciona con Knockout.js, ya que Knockout escuchará ese controlador de eventos. En otras palabras, cuando el usuario hace clic en la casilla de verificación falsa, el modelo de Knockout enlazado se actualiza. Sin embargo, lo que no funciona es actualizar el modelo. Si llamo:
model.SomeValue(!curValue); // SomeValue is bound to a checkbox, flip its value
El modelo se actualiza, pero la interfaz de usuario falsa no se actualiza. He rastreado este problema hasta el código en ko.bindingHandlers.checked.update
que hace lo siguiente:
// When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
element.checked = value;
Básicamente, se establece la propiedad element.checked
, pero no se desencadenan eventos. Por lo tanto, nunca se llama a la función _changeEvent
. Entonces, implementé mi propia función ko.bindingHandlers.checked.update
, que es una copia del incorporado. En teoría, esto es todo lo que debería hacer:
ko.bindingHandlers.checked.update = function (element, valueAccessor)
{
var value = ko.utils.unwrapObservable(valueAccessor());
if (element.type == "checkbox")
{
if (value instanceof Array)
{
// When bound to an array, the checkbox being checked represents its value being present in that array
element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
}
else
{
// When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
//element.checked = value;
$(element).prop(''checked'', value).trigger(''change''); // <--- this should work!
}
}
else if (element.type == "radio")
{
element.checked = (element.value == value);
}
};
En lugar de establecer element.checked
, en su lugar, .prop(''checked'', value)
y desencadenar el evento change. Sin embargo, esto no está funcionando. Esto es lo que sé hasta ahora:
- Si
$(element).prop(''checked'', value).trigger(''change'');
Knockout.js de la ecuación,$(element).prop(''checked'', value).trigger(''change'');
funciona perfectamente bien Entonces, knockout.js está jodiendo con el evento de alguna forma. ¿Desenlazar ese controlador de eventos? - He confirmado que
$(element)
es lo mismo quethis.$input
en el código de enlace falso de la casilla de verificación. Puedo establecer otras propiedades de expansión en este elemento, y se muestran. - He intentado algunos enfoques para tratar de depurar en Knockout.js y jQuery para ver si el evento todavía está vinculado, sin embargo, no he llegado a ninguna parte con este enfoque. Mi corazonada es que Knockout.js de alguna manera reemplazó el controlador de eventos de
change
con su propio interno, y se eliminaron los enlaces existentes. No he encontrado una manera de confirmar esto todavía.
Mi pregunta: Principalmente, estoy buscando una solución a este problema. ¿Knockout.js elimina los eventos de change
existentes que estaban allí antes de que se aplicara el modelo? ¿Cuáles son los próximos pasos para depurar este código y averiguar exactamente qué está pasando?
Intente activar y escuchar también un evento personalizado:
element.checked = value;
element.dispatchEvent(new Event("forcedChange"));
Intente crear el objeto de la casilla de verificación como un complemento jQuery y cree enlaces personalizados no deseados para vincularlo a su modelo de vista. Configuré un jsfiddle que hace justamente eso: http://jsfiddle.net/markhagers/zee4z/3/
$(function () {
var viewModel = {
truthyValue1: ko.observable(false),
truthyValue2: ko.observable(true)
};
ko.applyBindings(viewModel);
});
(Tuve que agregar este fragmento o mi respuesta no fue aceptada; consulte el jsfiddle para obtener el ejemplo completo del código). Lo siento, parece una gran cantidad de código, pero la parte del complemento era originalmente un plugin jquery independiente, con los enlaces eliminados añadidos más tarde, algún código en él ha quedado obsoleto por los enlaces knockout. Se puede poner en su propio archivo para reducir el desorden. La creación de enlaces personalizados está documentada en el sitio web knockout.
Parece que el problema que tienes no se debe a un conflicto en eventos de knockout y otro conjunto de códigos (por ejemplo, cambiar el CSS de las casillas de verificación) sino debido a un conflicto en cómo el hilo de Javascript y los eventos en el hilo, se manejan.
Javascript se maneja en el navegador como un único hilo: todos los procesos se ejecutan secuencialmente y cualquier evento que interrumpa la secuencia puede causar que su evento falle. Si puede llamar a cada una de las funciones de forma independiente, pero hay interferencia cuando incluye ambas en su secuencia de comandos, el evento onclick o onmousedown que desencadena el cambio de CSS para las casillas de verificación puede interferir con la secuencia de comandos knockout. En este "hilo único", todos los eventos pueden influir en la ejecución de otro, incluso el movimiento del mouse.
Este ejemplo ( http://ejohn.org/blog/how-javascript-timers-work/ ) demuestra esta idea utilizando temporizadores, pero creo que su situación actual podría estar sufriendo el mismo problema.
Para resolverlo, puede escribir una breve función como:
función doBoth (casilla de verificación) {
$ ("# checkboxid"). addclass ("prettychecked");
$ ("# checkboxid"). prop (''verificado'', verdadero);
}
El punto importante es que cada acción tiene lugar en secuencia sin la posibilidad de que un evento de mouse se interrumpa.
¡Espero que esto ayude!
Solo lluvia de ideas, pero ¿se desarrolló este control de terceros usando espacios de nombres de eventos? Podría ser como tu sugieres, ese knockout y este control están peleando por el evento de ''cambio'' predeterminado de alguna manera. Cuando desarrollo widgets y aplicaciones, siempre uso espacios de nombres de eventos cuando vincula eventos usando jQuery.
Ejemplo utilizando un espacio de nombres ''myWidget'':
$(''#element'').on(''change.myWidget'', function(e) {
// handle the event
}
Sin embargo, podría estar completamente fuera de lugar.
Solo un pensamiento rápido. Sus diseñadores parecen activar el evento de hacer clic y cambiar en esta línea:
self.$input.click().trigger(''change'');
¿No podría ser algo para probar? ¿Tal vez está relacionado con el evento click en lugar del evento change? Intenta esto en tu otra declaración:
$(element).prop(''checked'', value).click().trigger(''change''); // <--- this should work!
Lo que he entendido que .click () hace: imita un clic de ratón y al mismo tiempo desencadena el evento de clic (¿Duh?).
Sé que a veces (como en una radio o casilla de verificación) algunos codificadores vinculan el evento al clic en lugar de cambiar, ya que es más probable que se active en el momento correcto (en IE7 el evento de cambio se dispara de una manera realmente extraña).
Solo un pensamiento, podría ser algo para probar. ¡Espero eso ayude!
EDITAR: Vi una respuesta sobre espacios de nombres en tus eventos y eventos personalizados. Esto también podría ayudarlo, espero que no sea un cambio demasiado grande.
/ J.