Skip to content

Commit

Permalink
Make sure component parameters aren't confused with bindings; add tes…
Browse files Browse the repository at this point in the history
…t showing custom elements work with observable view models.
  • Loading branch information
mbest committed Apr 25, 2014
1 parent 73c36f0 commit 3994a15
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 13 deletions.
31 changes: 31 additions & 0 deletions spec/components/customElementBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ describe('Components: Custom elements', function() {
expect(suppliedParams).toEqual([{ nothing: null, num: 123, bool: true, obj: { abc: 123 }, str: 'mystr' }]);
});

it('Should not confuse parameters with bindings', function() {
this.restoreAfter(ko, 'getBindingHandler');
var bindings = [];
ko.getBindingHandler = function(bindingKey) {
bindings.push(bindingKey);
};

ko.components.register('test-component', {});
testNode.innerHTML = '<test-component params="value: value"></test-component>';
ko.applyBindings({value: 123}, testNode);

// The only binding it should look up is "component"
expect(bindings).toEqual(['component']);
});

it('Should update component when observable view model changes', function() {
ko.components.register('test-component', {
template: '<p>the value: <span data-bind="text: textToShow"></span></p>'
});

testNode.innerHTML = '<test-component params="textToShow: value"></test-component>';
var vm = ko.observable({ value: 'A' });
ko.applyBindings(vm, testNode);
jasmine.Clock.tick(1);
expect(testNode).toContainText("the value: A");

vm({ value: 'Z' });
jasmine.Clock.tick(1);
expect(testNode).toContainText("the value: Z");
});

it('Is possible to pass observable instances', function() {
ko.components.register('test-component', {
template: '<p>the observable: <span data-bind="text: receivedobservable"></span></p>',
Expand Down
18 changes: 10 additions & 8 deletions src/binding/expressionRewriting.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,16 @@ ko.expressionRewriting = (function () {
function callPreprocessHook(obj) {
return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true;
}
if (!callPreprocessHook(ko['getBindingHandler'](key)))
return;

if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
// For two-way bindings, provide a write method in case the value
// isn't a writable observable.
propertyAccessorResultStrings.push("'" + key + "':function(_z){" + writableVal + "=_z}");
if (!bindingParams) {
if (!callPreprocessHook(ko['getBindingHandler'](key)))
return;

if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
// For two-way bindings, provide a write method in case the value
// isn't a writable observable.
propertyAccessorResultStrings.push("'" + key + "':function(_z){" + writableVal + "=_z}");
}
}

// Values are wrapped in a function so that each value can be accessed independently
if (makeValueAccessors) {
val = 'function(){return ' + val + ' }';
Expand All @@ -130,6 +131,7 @@ ko.expressionRewriting = (function () {
var resultStrings = [],
propertyAccessorResultStrings = [],
makeValueAccessors = bindingOptions['valueAccessors'],
bindingParams = bindingOptions['bindingParams'],
keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ?
parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;

Expand Down
10 changes: 5 additions & 5 deletions src/components/customElements.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
var paramsAttribute = elem.getAttribute('params');

if (paramsAttribute) {
var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true }),
var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {
return ko.computed(paramValue, null, { 'disposeWhenNodeIsRemoved': elem });
return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
}),
result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
// Does the evaluation of the parameter value unwrap any observables?
Expand All @@ -52,13 +52,13 @@
// This means the component doesn't have to worry about multiple unwrapping.
return ko.computed(function() {
return ko.utils.unwrapObservable(paramValueComputed());
}, null, { 'disposeWhenNodeIsRemoved': elem });
}, null, { disposeWhenNodeIsRemoved: elem });
}
});

// Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called 'raw'
// Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'
// This is in case the developer wants to react to outer (binding) observability separately from inner
// (model value) observability seperately, or in case the model value observable has subobservables.
// (model value) observability, or in case the model value observable has subobservables.
if (!result.hasOwnProperty('$raw')) {
result['$raw'] = rawParamComputedValues;
}
Expand Down

0 comments on commit 3994a15

Please sign in to comment.