diff --git a/spec/bindingAttributeBehaviors.js b/spec/bindingAttributeBehaviors.js index edf441584..94fdb1c95 100644 --- a/spec/bindingAttributeBehaviors.js +++ b/spec/bindingAttributeBehaviors.js @@ -229,6 +229,19 @@ describe('Binding attribute syntax', function() { expect(testNode).toContainText("my value"); }); + it('Binding context should hide or not minify extra internal properties', function () { + testNode.innerHTML = "
"; + ko.applyBindings({}, testNode); + + var allowedProperties = ['$parents', '$root', 'ko', '$rawData', '$data', '$parentContext', '$parent']; + if (ko.utils.createSymbolOrString('') === '') { + allowedProperties.push('_subscribable'); + } + ko.utils.objectForEach(ko.contextFor(testNode.childNodes[0].childNodes[0]), function (prop) { + expect(allowedProperties).toContain(prop); + }); + }); + it('Should be able to retrieve the binding context associated with any node', function() { testNode.innerHTML = "
"; ko.applyBindings({ name: 'Bert' }, testNode.childNodes[0]); diff --git a/src/binding/bindingAttributeSyntax.js b/src/binding/bindingAttributeSyntax.js index 0e2955331..a68a14968 100755 --- a/src/binding/bindingAttributeSyntax.js +++ b/src/binding/bindingAttributeSyntax.js @@ -1,4 +1,7 @@ (function () { + // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294 + var contextSubscribable = ko.utils.createSymbolOrString('_subscribable'); + ko.bindingHandlers = {}; // The following element types will not be recursed into during binding. @@ -38,14 +41,14 @@ if (parentContext) { // When a "parent" context is given, register a dependency on the parent context. Thus whenever the // parent context is updated, this context will also be updated. - if (parentContext._subscribable) - parentContext._subscribable(); + if (parentContext[contextSubscribable]) + parentContext[contextSubscribable](); // Copy $root and any custom properties from the parent context ko.utils.extend(self, parentContext); // Because the above copy overwrites our own properties, we need to reset them. - self._subscribable = subscribable; + self[contextSubscribable] = subscribable; } else { self['$parents'] = []; self['$root'] = dataItem; @@ -97,7 +100,7 @@ // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in // the context object. if (subscribable.isActive()) { - self._subscribable = subscribable; + self[contextSubscribable] = subscribable; // Always notify because even if the model ($data) hasn't changed, other context properties might have changed subscribable['equalityComparer'] = null; @@ -115,7 +118,7 @@ ko.utils.arrayRemoveItem(nodes, node); if (!nodes.length) { subscribable.dispose(); - self._subscribable = subscribable = undefined; + self[contextSubscribable] = subscribable = undefined; } }); }; @@ -144,8 +147,8 @@ // Similarly to "child" contexts, provide a function here to make sure that the correct values are set // when an observable view model is updated. ko.bindingContext.prototype['extend'] = function(properties) { - // If the parent context references an observable view model, "_subscribable" will always be the - // latest view model object. If not, "_subscribable" isn't set, and we can use the static "$data" value. + // If the parent context references an observable view model, "contextSubscribable" will always be the + // latest view model object. If not, "contextSubscribable" isn't set, and we can use the static "$data" value. return new ko.bindingContext(inheritParentVm, this, null, function(self, parentContext) { ko.utils.extend(self, typeof(properties) == "function" ? properties() : properties); }); @@ -294,8 +297,8 @@ } ko.utils.domData.set(node, boundElementDomDataKey, {context: bindingContext}); - if (bindingContext._subscribable) - bindingContext._subscribable._addNode(node); + if (bindingContext[contextSubscribable]) + bindingContext[contextSubscribable]._addNode(node); } // Use bindings if given, otherwise fall back on asking the bindings provider to give us some bindings @@ -312,8 +315,8 @@ function() { bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext); // Register a dependency on the binding context to support observable view models. - if (bindings && bindingContext._subscribable) - bindingContext._subscribable(); + if (bindings && bindingContext[contextSubscribable]) + bindingContext[contextSubscribable](); return bindings; }, null, { disposeWhenNodeIsRemoved: node } diff --git a/src/utils.js b/src/utils.js index 5add45b86..76f57d85a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -611,6 +611,7 @@ ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap); ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll); ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem); ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes); +ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString); ko.exportSymbol('utils.extend', ko.utils.extend); ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost); ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);