2012-06-18 4 views
70

Ich erstelle eine Berechtigungsbenutzeroberfläche, ich habe eine Liste von Berechtigungen mit einer Auswahlliste neben jeder Berechtigung. Die Berechtigungen werden von einer beobachtbaren Array von Objekten dar, die zu einer Auswahlliste gebunden sind:Ereignis bei Auswahl mit Knockout-Bindung ändern, wie kann ich wissen, ob es sich um eine echte Änderung handelt

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

Nun das Problem ist folgende: die Änderung Ereignis ausgelöst wird, wenn die Benutzeroberfläche nur zum ersten Mal bevölkert. Ich rufe meine Ajax-Funktion auf, hole die Berechtigungsliste und erhöhe dann das Ereignis für jedes der Berechtigungselemente. Das ist wirklich nicht das Verhalten, das ich will. Ich möchte es angehoben werden, nur wenn ein Benutzer wirklich einen neuen Wert für die Erlaubnis in der Auswahlliste herausholt, wie kann ich das tun?

Antwort

32

Dies ist nur eine Vermutung, aber ich denke, es passiert weil level eine Nummer ist. In diesem Fall löst die value Bindung ein change Ereignis aus, um level mit dem Zeichenfolgenwert zu aktualisieren. Sie können dies beheben, indem Sie sicherstellen, dass level eine Zeichenfolge ist, mit der Sie beginnen können.

Darüber hinaus ist die "Knockout" -Methode, keine Event-Handler zu verwenden, sondern Observables und Subscriptions zu verwenden. Machen Sie level ein Observable und fügen Sie dann ein Abonnement hinzu, das ausgeführt wird, wenn sich level ändert.

+0

nach 2 Stunden graben die Ursache meines Formulars auslösen das "Change" -Ereignis nach dem Laden der Seite, hast du mich gerettet gespeichert. –

+0

Ihre Antwort gab mir eine Idee ... Ich änderte absichtlich den Typ, der von der URL kommt, so dass meine Subscribe-Funktion beim Laden der Seite ausgelöst würde (ich wollte eine URL-Variable verarbeiten, nicht nur wenn mein Dropdown eine Änderung auslöst). Gibt es eine weniger hacky Art, dies zu tun? – Jiffy

1

Schnell und schmutzig, ein einfaches Flag verwendet:

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 einem setTimeout() packen - Veranstaltungen sind async, vielleicht der letzte noch aussteht wenn applyBindings() bereits zurückgegeben wurde.

+0

Hallo danke für die Antwort, das wird nicht für mich arbeiten, weil ich die ko.applyBindings aufrufen, wenn das dom bereit ist, aber meine Berechtigungen werden zu einem späteren Zeitpunkt zu meinem Modell bevölkert ... so würde die Flagge sei wahr, aber das Problem würde immer noch passieren. –

4

Hier ist eine Lösung, die mit diesem seltsamen Verhalten helfen kann. Ich konnte keine bessere Lösung finden, als eine Schaltfläche zu setzen, um das Änderungsereignis manuell auszulösen.

EDIT: Vielleicht eine benutzerdefinierte wie diese Bindung könnte 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 in Ihrem ausgewählten Daten-bind-Attribut hinzufügen:

changeSelectValue: yourSelectValue 
+0

oh ... du meinst wie eine sichere Schaltfläche? .. nicht gut für mich .. ich brauche nur das zum arbeiten :) –

+0

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

+0

Hey Ingro, das wurde (falsch) als keine Antwort markiert. Ich habe Ihren ersten Satz neu formuliert, damit die Flagger nicht versehentlich denken, dass Sie eine Frage als Antwort posten. Hoffe das hilft! :) – jmort253

2

Wenn Sie einen beobachtbaren anstelle eines primitiven Werts verwenden, löst die Auswahl keine Änderungsereignisse bei der anfänglichen Bindung aus. Sie können weiterhin an das Änderungsereignis binden und nicht direkt an der Observablen teilnehmen.

3

Ich benutze diese benutzerdefinierte Bindung (basierend auf this Geige von RP Niemeyer, siehe seine Antwort auf this Frage), die sicherstellen, dass der numerische Wert ordnungsgemäß von String zu 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> 
74

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

Der Knockout-Ansatz subscription der Zugabe wird nicht in allen Fällen helfen, warum denn in den meisten des Modells werden wie diese

  1. init das Modell mit undefinierten Daten implementiert werden, strukturiert gerade (actual KO initilization)
  2. aktualisiert das Modell mit Anfangsdaten (logical init like load JSON , get data etc)
  3. Benutzer-Interaktion und Updates

Der eigentliche Schritt, den ich ist, Änderungen in 3 aufnehmen mag, aber in der zweiten Stufe subscription rufen bekommen, So ein besserer Weg ist, um Ereignisänderung wie

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

und erfasst das Ereignis hinzuzufügen, in permissionChanged Funktion

this.permissionChanged = function (obj, event) { 

    if (event.originalEvent) { //user changed 

    } else { // program changed 

    } 

} 
+4

Ich denke, das sollte als die richtige Antwort markiert werden. Ich hatte das genaue Szenario, wie in der Frage beschrieben, und ich prüfte Passing-Strings als der Schlüssel zu dem 'select' Element. Selbst wenn der Anfangswert der Auswahl einer der Optionen übereinstimmte, löste sie dennoch das Ereignis "Änderung" aus. Als ich diesen einfachen 'if'-Block im Handler implementierte, funktionierte alles wie erwartet. Probieren Sie diese Methode zuerst aus. Danke, @Sarath! – Pflugs

+0

Ich mag das Konzept der Unterscheidung zwischen Benutzer geändert und Programm geändert. Dies ist hilfreich in alltäglichen Fällen, insbesondere bei Knock-outs, bei denen Observable am ehesten ohne Interaktion mit dem Benutzer ihren Wert ändern können. – Rodiwa

+0

Stimmen Sie mit @Pfugs überein, differenzieren Sie zwischen den für mich bearbeiteten Ereignistypen und dies sollte die akzeptierte Antwort sein. –

0

ich hatte ein ähnliches Problem, und ich modifiziert nur den Event-Handler den Typ der variablen zu überprüfen. Der Typ wird erst festgelegt, nachdem der Benutzer einen Wert ausgewählt hat und nicht, wenn die Seite zum ersten Mal geladen wird.

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

Dies scheint für mich zu arbeiten.

0

Wenn Sie mit Knockout arbeiten, verwenden Sie die Schlüsselfunktionalität der beobachtbaren Funktionalität knockout.
Verwenden Sie ko.computed() Methode und tun und Ajax Aufruf innerhalb dieser Funktion auslösen.