2014-02-05 1 views
5

У меня есть нокаут-расширение, knockout-secure-binding, и мы столкнулись с an issue.Knockout binding value.update не вызывается с пользовательской привязкой и defineProperty

В частности, при использовании Object.defineProperty, так как knockout-es5 делает, value связывание update-й функция не вызываются, когда событие изменения инициируются на input.

Мои unit tests иллюстрируют особенности. Это работает:

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

Это (будучи как Нокаут-ES5 определяет свойства) не работает:

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

В последнем случае, как уже упоминалось, value.update не вызывается, когда input.dispatchEvent называется.

Пользовательская привязка возвращает свой valueAccessor, поэтому я ожидаю, что проблема связана с этим. Мне просто кажется особенно странным, что он будет работать с объектом, но не defineProperty.

ответ

6

Нокаут перезаписывает обязательные выдержки перед их обработкой, чтобы поддерживать «двусторонние привязки», чтобы включить функцию записи, которая позволяет обработчику обновлять значение, даже если оно не является наблюдаемым ». Эта часть делает определенные свойства Object.defineProperty в привязках.

Это реализуется в ko.expressionRewriting.preProcessBindings методы (source)

Этот метод превращает следующее связывающее выражение:

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

К следующему:

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

Примечания сгенерированного _ko_property_writers, который содержит код для установки не наблюдаемых проаутировок.

А вот source кода комментарий об этой волшебной собственности:

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

Так что вам просто нужно воспроизвести ту же логику в вашей convert_to_accessors функции: вам нужно создать новое свойство на result объекта с именем "_ko_property_writers" которые возвращают соответствующие писателю функции:

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

Отказ от ответственности: это не производство чтения y реализация! Это просто показывает, что нужно сделать. Хотя это и делает ваши тестовые тесты, они могут сломать другие части плагина. Вы также должны внимательно следить за правильной обработкой контекста, потому что использование value.parser.context.$data является довольно хаки.

+2

Awesome. [Очень полезно] (https://github.com/brianmhunt/knockout-secure-binding/commit/6263979f9dc111ad847d8f2b83bcfa6fa6f2453f). Приветствия. –

+0

Стоит отметить, что Knockout предоставляет только '_twoWayBindings' (т. Е. Не' twoWayBindings'). См. [KSB/issues # 29] (https://github.com/brianmhunt/knockout-secure-binding/issues/29). Приветствия. –

Смежные вопросы