2012-06-18 11 views
70

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 un conjunto observable de los objetos que se une a una lista de selección:change event on select with knockout binding, ¿cómo puedo saber si es un cambio real

<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 el siguiente: el evento de cambio se elevó cuando la interfaz de usuario está simplemente poblando 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 eleve solo cuando un usuario realmente selecciona un nuevo valor para el permiso en la lista de selección, ¿cómo puedo hacer eso?

Respuesta

32

Ésta es sólo una suposición, pero creo que está sucediendo porque level es un número. En ese caso, el enlace value activará un evento change para actualizar level con el valor de cadena. Puede solucionar esto, por lo tanto, asegurándose de que level es 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 level un observable y luego agregue una suscripción a él, que se ejecutará cada vez que level cambie.

+0

después de 2 horas de cavar la causa que mi formulario desencadene el evento de "cambio" después de cargar la página, supongo que me salvó. –

+0

Su respuesta me dio una idea ... cambié a propósito el tipo procedente de la url, por lo que mi función de suscripción se activaría en la carga de la página (quería que procesara una URL var, no solo cuando mi menú desplegable desencadenó un cambio). ¿Hay una forma menos hacky de hacer esto? – Jiffy

1

rápida y sucia, la utilización de un indicador 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, trate de envolver la última línea de un setTimeout() - eventos son asíncronas, así que tal vez el último que aún está pendiente when applyBindings() ya devuelto.

+0

hola gracias por la respuesta, esto no funcionará para mí porque llamo a ko.applyBindings cuando el dom está listo, pero mis permisos a se están llenando a mi modelo en una etapa posterior ... por lo que la bandera ser cierto, pero el problema aún ocurriría. –

4

Aquí hay una solución que puede ayudar con este comportamiento extraño. No pude encontrar una solución mejor que colocar un botón para activar manualmente el evento de cambio.

EDIT: Tal vez una unión como esta costumbre 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 selecto de datos se unen atribuir añadir:

changeSelectValue: yourSelectValue 
+0

oh ... ¿te refieres a un botón para guardar? ... no es bueno para mí ... realmente solo necesito que esto funcione :) –

+0

Edita la respuesta con una mejor solución (con suerte). – Ingro

+0

Hey Ingro, esto se marcó (incorrectamente) como no una respuesta. Reformulé tu oración inicial para que los banderines no piensen accidentalmente que estás publicando una pregunta como respuesta. ¡Espero que esto ayude! :) – jmort253

2

Si utiliza 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.

3

utilizo esta unión personalizado (basado en this violín por RP Niemeyer, ver su respuesta a this cuestión), lo que hace que el valor numérico se convierte correctamente a partir de cadenas a números (como lo sugiere la solución de Michael Mejor):

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 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> 
74

En realidad desea buscar si el evento es desencadenado por el usuario o el programa, y es obvio que el evento se activará durante la inicialización.

El enfoque octavos de final de la adición subscription no ayudará en todos los casos, por qué, porque en la mayor parte del modelo se implementa de la siguiente

  1. init el modelo con datos indefinidos, simplemente estructurar (actual KO initilization)
  2. actualizar el modelo con los datos iniciales (logical init like load JSON , get data etc)
  3. la interacción del usuario y actualizaciones

El paso real que queremos capturar son los cambios en 3, pero en la segunda etapa subscription conseguirá llamar, lo tanto una mejor manera es añadir al cambio evento como

<select data-bind="value: level, event:{ change: $parent.permissionChanged}"> 

y detecta el evento en permissionChanged función

this.permissionChanged = function (obj, event) { 

    if (event.originalEvent) { //user changed 

    } else { // program changed 

    } 

} 
+4

Creo que esto debería marcarse como la respuesta correcta. Tenía el escenario exacto como se describe en la pregunta, y probé pasar cadenas como la clave para el elemento 'select'. Sin embargo, incluso cuando el valor inicial de la selección coincidía con una de las opciones, todavía activaba el evento 'change'. Cuando implementé este simple bloque 'if' en el controlador, todo funcionó como se esperaba. Pruebe este método primero. Gracias, @Sarath! – Pflugs

+0

Me gusta el concepto de diferenciar entre el cambio de usuario y el programa. Esto es útil en casos cotidianos, especialmente con knockout donde los observables tienen más probabilidades de cambiar el valor sin ninguna interacción del usuario. – Rodiwa

+0

Estoy de acuerdo con @Pflugs, diferenciando entre los tipos de eventos trabajados para mí y esta debería ser la respuesta aceptada. –

0

que tenía un problema similar y que acaba de modificar el controlador de eventos para comprobar 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í.

0

Si está trabajando usando Knockout, use la funcionalidad clave de la funcionalidad observable.
Utilice el método ko.computed() y ejecute y active la llamada ajax dentro de esa función.

Cuestiones relacionadas