knockout.js - change - knockout js tutorial español
cambiar evento en seleccionar con enlace knockout, ¿cómo puedo saber si es un cambio real (8)
Estoy construyendo una IU de permisos, tengo una lista de permisos con una lista de selección al lado de cada permiso. Los permisos están representados por una matriz observable de objetos que están vinculados a una lista de selección:
<div data-bind="foreach: permissions">
<div class="permission_row">
<span data-bind="text: name"></span>
<select data-bind="value: level, event:{ change: $parent.permissionChanged}">
<option value="0"></option>
<option value="1">R</option>
<option value="2">RW</option>
</select>
</div>
</div>
Ahora el problema es este: el evento de cambio se levanta cuando la interfaz de usuario acaba de poblar por primera vez. Llamo a mi función ajax, obtengo la lista de permisos y luego el evento se genera para cada uno de los elementos de permiso. Este no es realmente el comportamiento que quiero. Quiero que se plantee solo cuando un usuario realmente selecciona un nuevo valor para el permiso en la lista de selección , ¿cómo puedo hacer eso?
Aquí hay una solución que puede ayudar con este extraño comportamiento. No pude encontrar una solución mejor que colocar un botón para activar manualmente el evento de cambio.
EDITAR: Tal vez un enlace personalizado como este podría ayudar:
ko.bindingHandlers.changeSelectValue = {
init: function(element,valueAccessor){
$(element).change(function(){
var value = $(element).val();
if($(element).is(":focus")){
//Do whatever you want with the new value
}
});
}
};
Y en su atributo de vinculación de datos seleccionado, agregue:
changeSelectValue: yourSelectValue
En realidad , desea averiguar si el evento es desencadenado por el usuario o por el programa , y es obvio que el evento se activará durante la inicialización.
El enfoque de nocaut de agregar subscription
no ayudará en todos los casos, porque porque en la mayoría de los modelos se implementará así
- inicie el modelo con datos no definidos, solo estructura
(actual KO initilization)
- actualizar el modelo con datos iniciales
(logical init like load JSON , get data etc)
- Interacción y actualizaciones del usuario
El paso real que queremos capturar es el cambio en 3, pero en el segundo paso la subscription
recibirá una llamada, por lo que una mejor manera es agregar al cambio de evento como
<select data-bind="value: level, event:{ change: $parent.permissionChanged}">
y detectó el evento en la función permissionChanged
this.permissionChanged = function (obj, event) {
if (event.originalEvent) { //user changed
} else { // program changed
}
}
Esto es solo una suposición, pero creo que está sucediendo porque el level
es un número. En ese caso, el enlace de value
activará un evento de change
para actualizar el level
con el valor de cadena. Puede solucionar esto, por lo tanto, asegurándose de que el level
sea una cadena para comenzar.
Además, la forma más "Knockout" de hacer esto es no usar manejadores de eventos, sino usar observables y suscripciones. Haga que el level
observable y luego agregue una suscripción, que se ejecutará cada vez que el level
cambie.
Rápido y sucio, utilizando una bandera simple:
var bindingsApplied = false;
var ViewModel = function() {
// ...
this.permissionChanged = function() {
// ignore, if flag not set
if (!flag) return;
// ...
};
};
ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true
Si esto no funciona, intente empaquetar la última línea en un setTimeout () - los eventos son asíncronos, por lo que quizás el último aún esté pendiente cuando applyBindings () ya se haya devuelto.
Si está trabajando usando Knockout, use la funcionalidad clave de knockout de funcionalidad observable.
Use el método ko.computed()
y ko.computed()
y active una llamada ko.computed()
dentro de esa función.
Si usa un valor observable en lugar de uno primitivo, la selección no generará eventos de cambio en el enlace inicial. Puede seguir vinculando al evento de cambio, en lugar de suscribirse directamente al observable.
Tuve un problema similar y acabo de modificar el controlador de eventos para verificar el tipo de la variable. El tipo solo se establece después de que el usuario selecciona un valor, no cuando la página se carga por primera vez.
self.permissionChanged = function (l) {
if (typeof l != ''undefined'') {
...
}
}
Esto parece funcionar para mí.
Utilizo este enlace personalizado (basado en this violín por RP Niemeyer, veo su respuesta a this pregunta), que asegura que el valor numérico se convierta correctamente de cadena en número (como lo sugiere la solución de Michael Best):
Javascript:
ko.bindingHandlers.valueAsNumber = {
init: function (element, valueAccessor, allBindingsAccessor) {
var observable = valueAccessor(),
interceptor = ko.computed({
read: function () {
var val = ko.utils.unwrapObservable(observable);
return (observable() ? observable().toString() : observable());
},
write: function (newValue) {
observable(newValue ? parseInt(newValue, 10) : newValue);
},
owner: this
});
ko.applyBindingsToNode(element, { value: interceptor });
}
};
Ejemplo de HTML:
<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
<option value="0"></option>
<option value="1">R</option>
<option value="2">RW</option>
</select>