cambio de evento en la selección con enlace de eliminación, ¿cómo puedo saber si es un cambio real?


87

Estoy creando una interfaz de usuario de permisos, tengo una lista de permisos con una lista de selección junto a 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 el siguiente: el evento de cambio se genera cuando la interfaz de usuario se completa por primera vez. Llamo a mi función ajax, obtengo la lista de permisos y luego se genera el evento para cada uno de los elementos de permiso. Este no es realmente el comportamiento que quiero. Quiero que se genere solo cuando un usuario realmente selecciona un nuevo valor para el permiso en la lista de selección , ¿cómo puedo hacerlo?

Respuestas:


107

En realidad , desea saber si el evento lo activa el usuario o el programa , y es obvio que el evento se activará durante la inicialización.

El enfoque de nocaut de agregar subscriptionno ayudará en todos los casos, porque en la mayor parte del modelo se implementará así

  1. inicie el modelo con datos indefinidos, solo estructura (actual KO initilization)
  2. actualizar el modelo con datos iniciales (logical init like load JSON , get data etc)
  3. Interacción del usuario y actualizaciones

El paso real que queremos capturar son los cambios en 3, pero en el segundo paso subscriptionrecibirá una llamada, por lo que una mejor manera es agregar un cambio de evento como

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

y detectó el evento en permissionChangedfunción

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
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 del selectelemento. Sin embargo, incluso cuando el valor inicial de la selección coincidía con una de las opciones, aún desencadenaba el changeevento. Cuando implementé este ifbloque simple en el controlador, todo funcionó como se esperaba. Pruebe este método primero. ¡Gracias, @Sarath!
Pflugs

Me gusta el concepto de diferenciar entre usuario cambiado y programa cambiado. Esto es útil en casos cotidianos, especialmente con knockout donde es más probable que los observables cambien de valor sin interacción del usuario.
rodiwa

De acuerdo con @Pflugs, diferenciar entre los tipos de eventos funcionó para mí y esta debería ser la respuesta aceptada.
Emma Middlebrook

1
def debería ser una respuesta aceptada ... la propiedad de tipo de evento podría usarse para diferenciar la activación de la premisiónChanged
caniaskyouaquestion

1
También usé este método para detectar si un usuario estaba cambiando una selección en lugar de otro disparador. En mi caso, la actualización de los valores de las opciones a través del enlace "opciones" desencadenó el evento "cambiar".
Nath

32

Esto es solo una suposición, pero creo que está sucediendo porque leveles un número. En ese caso, el valueenlace activará un changeevento para actualizar levelcon el valor de la cadena. Por lo tanto, puede solucionar esto asegurándose de que levelsea ​​una cadena para comenzar.

Además, la forma más "Knockout" de hacer esto es no usar controladores de eventos, sino usar observables y suscripciones. Haga levelun observable y luego agregue una suscripción, que se ejecutará cada vez que levelcambie.


después de 2 horas de investigar la causa de que mi formulario activara el evento de 'cambio' después de cargar la página, supongo que me salvó.
Samih A

Su respuesta me dio una idea ... Cambié intencionalmente el tipo proveniente de la URL, por lo que mi función de suscripción se activará al cargar la página (quería que procesara una var de URL, no solo cuando mi menú desplegable activaba un cambio). ¿Hay alguna forma menos hacker de hacer esto?
Jiffy

4

Aquí hay una solución que puede ayudar con este extraño comportamiento. No pude encontrar una mejor solución 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 enlace de datos seleccionado agregue:

changeSelectValue: yourSelectValue

oh ... ¿Quieres decir como un botón Save .. no es bueno para mí .. yo en realidad sólo necesito que esto funcione :)?
chico Schaller

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

Hola 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

@ jmort253: lo siento, fue mi culpa. Es mejor no responder con "Yo también tengo el mismo problema", ya que parece que estás preguntando algo más. En su lugar, proporcione directamente una solución a una respuesta y explique cómo funciona.
Qantas 94 Heavy

1
Si lo siento, mi error. Fue una de las primeras respuestas que publiqué en stackoverflow y no conocía todas las reglas. ¡Gracias por editar!
Ingro

3

Utilizo este enlace personalizado (basado en este violín de RP Niemeyer, vea su respuesta a esta pregunta), que asegura que el valor numérico se convierta correctamente de cadena en número (como 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 });
    }
};

HTML de ejemplo:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>

2

Si usa un valor observable en lugar de uno primitivo, la selección no generará eventos de cambio en el enlace inicial. Puede seguir vinculándose al evento de cambio, en lugar de suscribirse directamente al observable.


1

Rápido y sucio, utilizando una simple bandera:

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 ajustar la última línea en un setTimeout (); los eventos son asíncronos, por lo que tal vez el último aún esté pendiente cuando applyBindings () ya regresó.


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

0

Tuve un problema similar y acabo de modificar el controlador de eventos para verificar el tipo de 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

utilizar esta:

this.permissionChanged = function (obj, event) {

    if (event.type != "load") {

    } 
}

0

Prueba este:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 

El segundo parámetro no era un índice, para mí era un evento.
sairfan

@sairfan agrega algunos parámetros a la función y encuentra de qué parámetro obtienes el evento usando el depurador
Chanaka Sampath

-1

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.