2014-02-05 9 views
5

Ich habe eine Knockout-Erweiterung, knockout-secure-binding, und wir haben an issue angetroffen.Knockout binding value.update nicht mit benutzerdefinierten Bindung aufgerufen und defineProperty

Insbesondere bei Object.defineProperty Verwendung als knockout-es5 der Fall ist, die value verbindlichen update Funktion wird nicht aufgerufen, wenn ein Änderungsereignis auf einem input ausgelöst wird.

Meine unit tests veranschaulichen die Besonderheit. Dies funktioniert:

it("reads an input `value` binding", function() { 
    var input = document.createElement("input"), 
     evt = new CustomEvent("change"), 
     context = { vobs: ko.observable() }; 
    input.setAttribute("data-sbind", "value: vobs") 
    ko.applyBindings(context, input) 
    input.value = '273-9164' 
    input.dispatchEvent(evt) 
    assert.equal(context.vobs(), '273-9164') 
}) 

Das (die wie Knockout-ES5 definiert Eigenschaften) funktioniert nicht:

it("reads an input `value` binding for a defineProperty", function() { 
    // see https://github.com/brianmhunt/knockout-secure-binding/issues/23 
    var input = document.createElement("input"), 
     evt = new CustomEvent("change"), 
     obs = ko.observable(), 
     context = { }; 
    Object.defineProperty(context, 'pobs', { 
     configurable: true, 
     enumerable: true, 
     get: obs, 
     set: obs 
    }); 
    input.setAttribute("data-sbind", "value: pobs") 
    ko.applyBindings(context, input) 
    input.value = '273-9164' 
    input.dispatchEvent(evt) 
    assert.equal(context.pobs, '273-9164') 
}) 

Im letzteren Fall, wie erwähnt, value.update nicht aufgerufen wird, wenn input.dispatchEvent genannt wird.

Die benutzerdefinierte Bindung gibt valueAccessor zurück, also erwarte ich, dass das Problem damit zusammenhängt. Es fällt mir gerade besonders auf, dass es mit einer Objekteigenschaft funktioniert, aber nicht defineProperty.

Antwort

6

Knockout schreibt die Bindung vor der Verarbeitung neu, um "bidirektionale Bindungen mit einer Schreibfunktion zu unterstützen, die es dem Handler ermöglichen, den Wert zu aktualisieren, auch wenn er nicht beobachtbar ist." Dieser Teil macht die Object.defineProperty definierten Eigenschaften in den Bindungen arbeiten.

Dies wird in der ko.expressionRewriting.preProcessBindings Verfahren (source)

Diese Methode wendet die folgende Bindungsausdruck umgesetzt:

data-bind="value: pobs, checked: vobs" 

auf die folgenden:

"'value':function(){return pobs },'checked':function(){return vobs },'_ko_property_writers':function(){return {'value':function(_z){pobs=_z},'checked':function(_z){vobs=_z}} }" 

Notiere die _ko_property_writers erzeugt, die enthält der Code für die Einstellung der nicht beobachtbaren Funktionen.

Und hier ist der source Code Kommentar zu dieser magischen Eigenschaft:

// For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an 
// undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official 
// public API, and we reserve the right to remove it at any time if we create a real public property writers API. 

So brauchen Sie nur die gleiche Logik in Ihrer convert_to_accessors Funktion zu reproduzieren: Sie eine neue Eigenschaft auf dem result Objekt erstellen, müssen "_ko_property_writers" benannt die Rückkehr der entsprechenden Schreiber Funktionen:

Parser.prototype.convert_to_accessors = function (result) { 
    var propertyWriters = {}; 
    ko.utils.objectForEach(result, function (name, value) { 
     if (value instanceof Identifier || value instanceof Expression) { 
     result[name] = function expidAccessor() { 
      // expression or identifier accessir 
      return value.get_value(); 
     }; 
     if (ko.expressionRewriting.twoWayBindings[name]) { 
      var token = value.token; 
      var context = value.parser.context.$data; 
      propertyWriters[name] = function(_z) { 
       context[token] = _z; 
      }; 
     } 
     } else if (typeof(value) != 'function') { 
     result[name] = function constAccessor() { 
      return value; 
     }; 
     } 
    }); 
    if (Object.keys(propertyWriters).length > 0) 
     result["_ko_property_writers"] = function() { 
      return propertyWriters; 
     } 
    return result; 
}; 

Haftungsausschluss: dies ist keine Produktion bereit ist i Durchführung! Es zeigt nur die Idee was zu tun ist. Obwohl beide Tests durchgeführt werden, können andere Teile des Plugins beschädigt werden. Sie sollten auch besonders auf die korrekte Kontextbehandlung achten, da die Verwendung von value.parser.context.$data irgendwie hacky ist.

+2

Ehrfürchtig. [Sehr hilfreich] (https://github.com/brianmhunt/knockout-secure-binding/commit/6263979f9dc111ad847d8f2b83bcfa6fa6f2453f). Prost. –

+0

Es ist erwähnenswert, dass Knockout nur '_TwoWayBindings' (d. H. Nicht' TwoWayBindings') darstellt. Siehe [KSB/Ausgaben Nr. 29] (https://github.com/brianmhunt/knockout-secure-binding/issues/29). Prost. –

Verwandte Themen