Skip to content

Commit

Permalink
Merged @markbadley's fix for dependent observables from 2.0, closing S…
Browse files Browse the repository at this point in the history
  • Loading branch information
sagacity committed Aug 24, 2011
1 parent e289d06 commit 0b5ebfc
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 102 deletions.
13 changes: 9 additions & 4 deletions knockout.mapping.js
Expand Up @@ -180,10 +180,15 @@ ko.exportProperty = function (owner, publicName, object) {

function withProxyDependentObservable(callback) {
var localDO = ko.dependentObservable;
ko.dependentObservable = function() {
var options = arguments[2] || {};
options.deferEvaluation = true;
var realDependentObservable = new realKoDependentObservable(arguments[0], arguments[1], options);
ko.dependentObservable = function(read, owner, options) {
options = options || {};

if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's
options = read;
}

options.deferEvaluation = true; // will either set for just options, or both read/options.
var realDependentObservable = new realKoDependentObservable(read, owner, options);
realDependentObservable.__ko_proto__ = realKoDependentObservable;
return realDependentObservable;
}
Expand Down
100 changes: 2 additions & 98 deletions spec/mappingBehaviors.js
Expand Up @@ -559,71 +559,6 @@ describe('Mapping', {
value_of(result.someProp.owner === result).should_be(true);
},

'ko.mapping.fromJS should handle interdependent dependent observables in objects': function() {
var obj = {
a: { a1: "a1" },
b: { b1: "b1" }
}

var dependencyInvocations = [];

var result = ko.mapping.fromJS(obj, {
a: {
create: function(options) {
return {
a1: ko.observable(options.data.a1),
observeB: ko.dependentObservable(function() {
dependencyInvocations.push("a");
return options.parent.b.b1();
})
}
}
},
b: {
create: function(options) {
return {
b1: ko.observable(options.data.b1),
observeA: ko.dependentObservable(function() {
dependencyInvocations.push("b");
return options.parent.a.a1();
})
}
},
}
});

value_of(result.a.observeB()).should_be("b1");
value_of(result.b.observeA()).should_be("a1");
},

'ko.mapping.fromJS should handle dependent observables in arrays': function() {
var obj = {
items: [
{ id: "a" },
{ id: "b" }
]
}

var dependencyInvocations = 0;

var result = ko.mapping.fromJS(obj, {
"items": {
create: function(options) {
return {
id: ko.observable(options.data.id),
observeParent: ko.dependentObservable(function() {
dependencyInvocations++;
return options.parent().length;
})
}
}
}
});

value_of(result.items()[0].observeParent()).should_be(2);
value_of(result.items()[1].observeParent()).should_be(2);
},

'ko.mapping.fromJS should send relevant create callbacks': function () {
var items = [];
var index = 0;
Expand Down Expand Up @@ -1497,38 +1432,7 @@ describe('Mapping', {
value_of(mapped.__ko_mapping__.mappedProperties.b).should_be(true);
},

'nested calls to mapping should not revert proxyDependentObservable multiple times': function() {
var vmjs = {
"inner1": {
"inner2": {
}
}
}
var vm = undefined;
var mapping = {
"inner1": {
"create": function(options) {
//use the same mapping object to map inner2
var that = ko.mapping.fromJS(options.data, mapping);
that.DOprop = ko.dependentObservable(function() {
// if the DO is evaluated straight away, this will return undefined
return vm;
});
return that;
}
},
"inner2": {
"create": function(options) {
var that = ko.mapping.fromJS(options.data);
return that;
}
}
};
var vm = ko.mapping.fromJS(vmjs, mapping);
value_of(vm.inner1.DOprop()).should_be(vm);
},

'ko.mapping.updateFromJS should merge options from subsequent calls': function() {
'ko.mapping.fromJS should merge options from subsequent calls': function() {
var obj = ['a'];

var result = ko.mapping.fromJS(obj, { dummyOption1: 1 });
Expand Down Expand Up @@ -1557,4 +1461,4 @@ describe('Mapping', {
}

});


153 changes: 153 additions & 0 deletions spec/proxyDependentObservableBehaviors.js
@@ -0,0 +1,153 @@
describe('ProxyDependentObservable', {
before_each: function() {
ko.mapping.resetDefaultOptions();
},

'ko.mapping.fromJS should handle interdependent dependent observables in objects': function() {
var obj = {
a: { a1: "a1" },
b: { b1: "b1" }
}

var dependencyInvocations = [];

var result = ko.mapping.fromJS(obj, {
a: {
create: function(options) {
return {
a1: ko.observable(options.data.a1),
observeB: ko.dependentObservable(function() {
dependencyInvocations.push("a");
return options.parent.b.b1();
})
}
}
},
b: {
create: function(options) {
return {
b1: ko.observable(options.data.b1),
observeA: ko.dependentObservable(function() {
dependencyInvocations.push("b");
return options.parent.a.a1();
})
}
},
}
});

value_of(result.a.observeB()).should_be("b1");
value_of(result.b.observeA()).should_be("a1");
},

'ko.mapping.fromJS should handle interdependent dependent observables with read/write callbacks in objects': function() {
var obj = {
a: { a1: "a1" },
b: { b1: "b1" }
}

var dependencyInvocations = [];

var result = ko.mapping.fromJS(obj, {
a: {
create: function(options) {
return {
a1: ko.observable(options.data.a1),
observeB: ko.dependentObservable({
read: function() {
dependencyInvocations.push("a");
return options.parent.b.b1();
},
write: function(value) {
options.parent.b.b1(value);
}
})
}
}
},
b: {
create: function(options) {
return {
b1: ko.observable(options.data.b1),
observeA: ko.dependentObservable({
read: function() {
dependencyInvocations.push("b");
return options.parent.a.a1();
},
write: function(value) {
options.parent.a.a1(value);
}
})
}
},
}
});

value_of(result.a.observeB()).should_be("b1");
value_of(result.b.observeA()).should_be("a1");

result.a.observeB("b2");
result.b.observeA("a2");
value_of(result.a.observeB()).should_be("b2");
value_of(result.b.observeA()).should_be("a2");
},

'ko.mapping.fromJS should handle dependent observables in arrays': function() {
var obj = {
items: [
{ id: "a" },
{ id: "b" }
]
}

var dependencyInvocations = 0;

var result = ko.mapping.fromJS(obj, {
"items": {
create: function(options) {
return {
id: ko.observable(options.data.id),
observeParent: ko.dependentObservable(function() {
dependencyInvocations++;
return options.parent().length;
})
}
}
}
});

value_of(result.items()[0].observeParent()).should_be(2);
value_of(result.items()[1].observeParent()).should_be(2);
},

'nested calls to mapping should not revert proxyDependentObservable multiple times': function() {
var vmjs = {
"inner1": {
"inner2": {
}
}
}
var vm = undefined;
var mapping = {
"inner1": {
"create": function(options) {
//use the same mapping object to map inner2
var that = ko.mapping.fromJS(options.data, mapping);
that.DOprop = ko.dependentObservable(function() {
// if the DO is evaluated straight away, this will return undefined
return vm;
});
return that;
}
},
"inner2": {
"create": function(options) {
var that = ko.mapping.fromJS(options.data);
return that;
}
}
};
var vm = ko.mapping.fromJS(vmjs, mapping);
value_of(vm.inner1.DOprop()).should_be(vm);
}
});
1 change: 1 addition & 0 deletions spec/runner.html
Expand Up @@ -22,6 +22,7 @@
<script type="text/javascript" src="../knockout.mapping.js"></script>

<script type="text/javascript" src="mappingBehaviors.js"></script>
<script type="text/javascript" src="proxyDependentObservableBehaviors.js"></script>
</head>
<body>
<div style="display:none;"><p>A</p><p>B</p></div>
Expand Down

0 comments on commit 0b5ebfc

Please sign in to comment.