Modifica evento su selezione con associazione ad eliminazione diretta, come posso sapere se si tratta di un vero cambiamento?


87

Sto creando un'interfaccia utente per le autorizzazioni, ho un elenco di autorizzazioni con un elenco di selezione accanto a ciascuna autorizzazione. I permessi sono rappresentati da una matrice osservabile di oggetti che sono legati a un elenco di selezione:

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

Ora il problema è questo: l'evento di modifica viene generato quando l'interfaccia utente viene popolata per la prima volta. Chiamo la mia funzione ajax, ottengo l'elenco delle autorizzazioni e quindi l'evento viene generato per ciascuno degli elementi di autorizzazione. Questo non è davvero il comportamento che voglio. Voglio che venga generato solo quando un utente seleziona davvero un nuovo valore per l'autorizzazione nell'elenco di selezione , come posso farlo?

Risposte:


109

In realtà vuoi scoprire se l'evento è attivato dall'utente o dal programma , ed è ovvio che l'evento si attiverà durante l'inizializzazione.

L'approccio knockout dell'aggiunta subscriptionnon aiuta in tutti i casi, perché perché nella maggior parte del modello verrà implementato in questo modo

  1. avvia il modello con dati non definiti, solo struttura (actual KO initilization)
  2. aggiornare il modello con i dati iniziali (logical init like load JSON , get data etc)
  3. Interazione dell'utente e aggiornamenti

Il passaggio effettivo che vogliamo catturare sono le modifiche in 3, ma nel secondo passaggio subscriptionotterremo la chiamata, quindi un modo migliore è aggiungere alla modifica dell'evento come

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

e ha rilevato l'evento in permissionChangedfunzione

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
Penso che questo dovrebbe essere contrassegnato come la risposta corretta. Avevo lo scenario esatto descritto nella domanda e ho testato il passaggio di stringhe come chiave per l' selectelemento. Tuttavia, anche quando il valore iniziale della selezione corrispondeva a una delle opzioni, ha comunque attivato l' changeevento. Quando ho implementato questo semplice ifblocco nel gestore, tutto ha funzionato come previsto. Prova prima questo metodo. Grazie, @Sarath!
Pflugs

Mi piace il concetto di differenziazione tra utente modificato e programma modificato. Ciò è utile nei casi di tutti i giorni, soprattutto con knockout in cui è più probabile che gli osservabili cambino valore senza alcuna interazione da parte dell'utente.
Rodiwa

D'accordo con @Pflugs, distinguere tra i tipi di eventi ha funzionato per me e questa dovrebbe essere la risposta accettata.
Emma Middlebrook

1
def dovrebbe essere una risposta accettata ... la proprietà del tipo di evento potrebbe essere utilizzata per differenziare l'attivazione di premissionChanged
caniaskyouaquestion

1
Questo metodo è stato utilizzato anche per rilevare se un utente stava modificando una selezione anziché un altro trigger. Nel mio caso l'aggiornamento dei valori delle opzioni tramite l'associazione "opzioni" ha attivato l'evento "modifica".
nath

32

Questa è solo un'ipotesi, ma penso che stia accadendo perché levelè un numero. In tal caso, l' valueassociazione attiverà un changeevento da aggiornare levelcon il valore di stringa. Puoi risolvere questo problema, quindi, assicurandoti che levelsia una stringa con cui iniziare.

Inoltre, il modo più "knockout" per farlo è non utilizzare gestori di eventi, ma utilizzare osservabili e sottoscrizioni. Crea levelun osservabile e quindi aggiungi una sottoscrizione, che verrà eseguita ogni volta che levelcambia.


dopo 2 ore di ricerca della causa che il mio modulo ha attivato l'evento 'modifica' dopo il caricamento della pagina, indovini mi hai salvato.
Samih A

La tua risposta mi ha dato un'idea ... Ho intenzionalmente cambiato il tipo proveniente dall'URL, quindi la mia funzione di iscrizione si sarebbe attivata al caricamento della pagina (volevo che elaborasse una variabile URL, non solo quando il mio menu a discesa ha attivato una modifica). C'è un modo meno hacker per farlo?
Jiffy

4

Ecco una soluzione che può aiutare con questo strano comportamento. Non sono riuscito a trovare una soluzione migliore che posizionare un pulsante per attivare manualmente l'evento di modifica.

EDIT: Forse un'associazione personalizzata come questa potrebbe aiutare:

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
            }

        });

    }
  };

E nel tuo attributo di associazione dati selezionato aggiungi:

changeSelectValue: yourSelectValue

oh ... intendi come un pulsante di salvataggio? .. non va bene per me .. ho davvero bisogno che funzioni :)
guy schaller

Modifica la risposta con una soluzione migliore (si spera).
Ingro

Ehi Ingro, questo è stato contrassegnato (erroneamente) come non una risposta. Ho riformulato la tua frase iniziale in modo che i segnalatori non pensino accidentalmente che stai pubblicando una domanda come risposta. Spero che sia di aiuto! :)
jmort253

@ jmort253: scusa, è stata colpa mia. È meglio non rispondere usando "Anch'io ho lo stesso problema", poiché sembra che tu stia chiedendo qualcos'altro. Fornisci invece direttamente una soluzione a una risposta e spiega come funziona.
Qantas 94 Heavy

1
Sì, scusa, errore mio. È stata una delle prime risposte che ho pubblicato su StackOverflow e non conoscevo tutte le regole. Grazie per la modifica!
Ingro

3

Uso questa associazione personalizzata (basata su questo violino di RP Niemeyer, vedi la sua risposta a questa domanda), che si assicura che il valore numerico sia correttamente convertito da stringa a numero (come suggerito dalla soluzione di 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 di esempio:

<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

Se si utilizza un valore osservabile invece di un valore primitivo, la selezione non genererà eventi di modifica al collegamento iniziale. Puoi continuare a legarti all'evento di cambiamento, invece di iscriverti direttamente all'osservabile.


1

Rapido e sporco, utilizzando una semplice bandiera:

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

Se questo non funziona, prova a racchiudere l'ultima riga in un setTimeout () - gli eventi sono asincroni, quindi forse l'ultimo è ancora in sospeso quando applyBindings () è già restituito.


ciao grazie per la risposta, questo non funzionerà per me perché chiamo ko.applyBindings quando il dom è pronto, ma i miei permessi vengono popolati sul mio modello in una fase successiva ... quindi la bandiera sarebbe vera ma il problema sarebbe ancora accaduto.
guy schaller

0

Ho avuto un problema simile e ho appena modificato il gestore di eventi per controllare il tipo di variabile. Il tipo viene impostato solo dopo che l'utente seleziona un valore, non quando la pagina viene caricata per la prima volta.

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

Questo sembra funzionare per me.


0

Usa questo:

this.permissionChanged = function (obj, event) {

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

    } 
}

0

Prova questo:

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

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

Il secondo parametro non era indice, per me era evento.
sairfan

@sairfan aggiunge alcuni parametri alla funzione e trova da quale parametro si ottiene l'evento utilizzando il debugger
Chanaka Sampath

-1

Se stai lavorando utilizzando Knockout, utilizza la funzionalità chiave di osservabile funzionalità knockout.
Usa il ko.computed()metodo e fai e attiva la chiamata ajax all'interno di quella funzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.