Änderungsereignis bei Auswahl mit Knockout-Bindung. Wie kann ich wissen, ob es sich um eine echte Änderung handelt?


87

Ich erstelle eine Benutzeroberfläche für Berechtigungen. Ich habe eine Liste mit Berechtigungen mit einer Auswahlliste neben jeder Berechtigung. Die Berechtigungen werden durch ein beobachtbares Array von Objekten dargestellt, die an eine Auswahlliste gebunden sind:

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

Das Problem ist nun folgendes: Das Änderungsereignis wird ausgelöst, wenn die Benutzeroberfläche zum ersten Mal gefüllt wird. Ich rufe meine Ajax-Funktion auf, erhalte die Berechtigungsliste und dann wird das Ereignis für jedes der Berechtigungselemente ausgelöst. Das ist wirklich nicht das Verhalten, das ich will. Ich möchte, dass es nur ausgelöst wird, wenn ein Benutzer wirklich einen neuen Wert für die Berechtigung in der Auswahlliste auswählt . Wie kann ich das tun?

Antworten:


104

Eigentlich möchten Sie herausfinden, ob das Ereignis vom Benutzer oder vom Programm ausgelöst wird , und es ist offensichtlich, dass dieses Ereignis während der Initialisierung ausgelöst wird.

Der Knockout-Ansatz des Hinzufügens subscriptionhilft nicht in allen Fällen, warum, weil in den meisten Fällen das Modell so implementiert wird

  1. Initiieren Sie das Modell mit undefinierten Daten, nur Struktur (actual KO initilization)
  2. Aktualisieren Sie das Modell mit den Anfangsdaten (logical init like load JSON , get data etc)
  3. Benutzerinteraktion und Updates

Der eigentliche Schritt, den wir erfassen möchten, sind Änderungen in 3, aber im zweiten Schritt subscriptionwird ein Aufruf angezeigt. Ein besserer Weg ist es also, Ereignisänderungen wie hinzuzufügen

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

und erkannte das Ereignis in permissionChangedFunktion

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
Ich denke, dies sollte als die richtige Antwort markiert werden. Ich hatte das genaue Szenario wie in der Frage beschrieben und habe die Übergabe von Zeichenfolgen als Schlüssel für das selectElement getestet . Selbst wenn der Anfangswert der Auswahl mit einer der Optionen übereinstimmte, wurde das changeEreignis dennoch ausgelöst . Als ich diesen einfachen ifBlock im Handler implementiert habe , hat alles wie erwartet funktioniert. Versuchen Sie zuerst diese Methode. Danke, @Sarath!
Pflugs

Ich mag das Konzept der Unterscheidung zwischen Benutzer geändert und Programm geändert. Dies ist in alltäglichen Fällen hilfreich, insbesondere beim Knockout, bei dem Observables den Wert am wahrscheinlichsten ohne Benutzerinteraktion ändern.
Rodiwa

Stimmen Sie mit @Pflugs überein und unterscheiden Sie zwischen den für mich verwendeten Ereignistypen. Dies sollte die akzeptierte Antwort sein.
Emma Middlebrook

1
def sollte eine akzeptierte Antwort sein ... Ereignistyp-Eigenschaft könnte verwendet werden, um das Auslösen der premissionChanged
caniaskyouaquestion

1
Diese Methode wurde auch verwendet, um festzustellen, ob ein Benutzer eine Auswahl anstelle eines anderen Auslösers geändert hat. In meinem Fall löste das Aktualisieren der Optionswerte über die Bindung "options" das Ereignis "change" aus.
nath

32

Dies ist nur eine Vermutung, aber ich denke, es passiert, weil leveles eine Zahl ist. In diesem Fall valuelöst die Bindung ein changeEreignis aus, das levelmit dem Zeichenfolgenwert aktualisiert werden soll. Sie können dies daher beheben, indem Sie sicherstellen, dasslevel es sich um eine Zeichenfolge handelt.

Darüber hinaus besteht die "Knockout" -Methode darin, keine Ereignishandler zu verwenden, sondern Observables und Abonnements zu verwenden. Machen Sie levelein Observable und fügen Sie ihm ein Abonnement hinzu, das bei jeder levelÄnderung ausgeführt wird.


Nachdem ich 2 Stunden lang die Ursache herausgefunden habe, hat mein Formular nach dem Laden der Seite das Ereignis "Ändern" ausgelöst.
Samih A

Ihre Antwort brachte mich auf eine Idee ... Ich habe den Typ, der von der URL kommt, absichtlich geändert, damit meine Abonnementfunktion beim Laden der Seite ausgelöst wird (ich wollte, dass eine URL-Variable verarbeitet wird, nicht nur, wenn mein Dropdown eine Änderung auslöst). Gibt es einen weniger hackigen Weg, dies zu tun?
Jiffy

4

Hier ist eine Lösung, die bei diesem seltsamen Verhalten helfen kann. Ich konnte keine bessere Lösung finden, als eine Schaltfläche zum manuellen Auslösen des Änderungsereignisses zu platzieren.

EDIT: Vielleicht könnte eine benutzerdefinierte Bindung wie diese helfen:

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
            }

        });

    }
  };

Und fügen Sie in Ihrem ausgewählten Datenbindungsattribut Folgendes hinzu:

changeSelectValue: yourSelectValue

oh ... du meinst wie ein Speichern-Button? .. nicht gut für mich .. ich brauche das wirklich nur, um zu funktionieren :)
Kerl schaller

Bearbeiten Sie die Antwort mit einer besseren Lösung (hoffentlich).
Ingro

Hey Ingro, dies wurde (fälschlicherweise) als keine Antwort markiert. Ich habe Ihren ersten Satz umformuliert, damit Flagger nicht versehentlich glauben, dass Sie eine Frage als Antwort posten. Hoffe das hilft! :)
jmort253

@ jmort253: Entschuldigung, das war meine Schuld. Es ist besser, nicht mit "Ich habe das gleiche Problem auch" zu antworten, da dies so aussieht, als würden Sie etwas anderes fragen. Geben Sie stattdessen direkt eine Lösung für eine Antwort an und erklären Sie, wie sie funktioniert.
Qantas 94 Heavy

1
Ja, tut mir leid, mein Fehler. Es war eine der ersten Antworten, die ich auf stackoverflow gepostet habe und nicht alle Regeln kannte. Danke für die Bearbeitung!
Ingro

3

Ich benutze diese benutzerdefinierte Bindung (basierend auf dieser Geige von RP Niemeyer, siehe seine Antwort auf darauf Frage), die sicherstellt, dass der numerische Wert ordnungsgemäß von Zeichenfolge in Zahl konvertiert wird (wie von der Lösung von Michael Best vorgeschlagen):

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 });
    }
};

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

2

Wenn Sie einen beobachtbaren Wert anstelle eines primitiven Werts verwenden, löst die Auswahl beim ersten Binden keine Änderungsereignisse aus. Sie können weiterhin an das Änderungsereignis binden, anstatt das Observable direkt zu abonnieren.


1

Schnell und schmutzig mit einer einfachen Flagge:

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

Wenn dies nicht funktioniert, versuchen Sie, die letzte Zeile in ein setTimeout () zu verpacken. Ereignisse sind asynchron. Möglicherweise steht das letzte noch aus, wenn applyBindings () bereits zurückgegeben wurde.


Hallo, danke für die Antwort, das wird bei mir nicht funktionieren, da ich die ko.applyBindings aufrufe, wenn der Dom fertig ist, aber meine Berechtigungen a zu einem späteren Zeitpunkt für mein Modell ausgefüllt werden ... also wäre die Flagge aber wahr Das Problem würde immer noch passieren.
Kerl schaller

0

Ich hatte ein ähnliches Problem und habe gerade den Ereignishandler geändert, um den Typ der Variablen zu überprüfen. Der Typ wird erst festgelegt, nachdem der Benutzer einen Wert ausgewählt hat, nicht beim ersten Laden der Seite.

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

Das scheint bei mir zu funktionieren.


0

benutze das:

this.permissionChanged = function (obj, event) {

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

    } 
}

0

Probier diese:

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

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

Der 2. Parameter war kein Index, für mich war es ein Ereignis.
Sairfan

@sairfan fügt der Funktion einige Parameter hinzu und findet heraus, von welchem ​​Parameter Sie mit dem Debugger ein Ereignis erhalten
Chanaka Sampath

-1

Wenn Sie mit Knockout arbeiten, verwenden Sie die Schlüsselfunktionalität von Knockout für beobachtbare Funktionen.
Verwenden Sie die ko.computed()Methode und führen Sie innerhalb dieser Funktion einen Ajax-Aufruf aus.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.