Fix {{#with}} over a data context that is mutated

In `Spacebars.With` we embox the data context. This commit makes
that emboxing happen modulo `safeEquals`. So now if you
{{#with}} over a helper that returns an object, any time
that helper gets invalidated we re-run the computations in the block.

Fixes #2046 (though notably that example mutates the data context
from within a helper, which could lead to other types of unintended
behavior; it's probably in this particular example -- the data context
just gets added properties)
This commit is contained in:
Avital Oliver
2014-04-19 00:21:01 -07:00
parent 76ded8feb2
commit 95aaa99312
4 changed files with 31 additions and 4 deletions

View File

@@ -712,3 +712,9 @@ Hi there!
{{bar}}
--}}
</template>
<template name="spacebars_test_with_mutated_data_context">
{{#with foo}}
{{value}}
{{/with}}
</template>

View File

@@ -1877,7 +1877,7 @@ Tinytest.add(
}
);
Tinytest.add("spacebars - templates - UI.toHTML", function (test) {
Tinytest.add("spacebars - template - UI.toHTML", function (test) {
// run once, verifying that autoruns are stopped
var once = function (tmplToRender, tmplForHelper, helper, val) {
var count = 0;
@@ -1919,3 +1919,24 @@ Tinytest.add(
test.equal(canonicalizeHtml(div.innerHTML), '');
}
);
// Originally reported at https://github.com/meteor/meteor/issues/2046
Tinytest.add(
"spacebars - template - {{#with}} with mutated data context",
function (test) {
var tmpl = Template.spacebars_test_with_mutated_data_context;
var foo = {value: 0};
var dep = new Deps.Dependency;
tmpl.foo = function () {
dep.depend();
return foo;
};
var div = renderToDiv(tmpl);
test.equal(canonicalizeHtml(div.innerHTML), '0');
foo.value = 1;
dep.changed();
Deps.flush();
test.equal(canonicalizeHtml(div.innerHTML), '1');
});

View File

@@ -216,7 +216,7 @@ Spacebars.dot = function (value, id1/*, id2, ...*/) {
Spacebars.With = function (argFunc, contentBlock, elseContentBlock) {
return UI.Component.extend({
init: function () {
this.v = UI.emboxValue(argFunc);
this.v = UI.emboxValue(argFunc, UI.safeEquals);
},
render: function () {
return UI.If(this.v, UI.With(this.v, contentBlock), elseContentBlock);

View File

@@ -38,7 +38,7 @@ UI.Unless = function (argFunc, contentBlock, elseContentBlock) {
// (Because then, they may be equal references to an object that was mutated,
// and we'll never know. We save only a reference to the old object; we don't
// do any deep-copying or diffing.)
var safeEquals = function (a, b) {
UI.safeEquals = function (a, b) {
if (a !== b)
return false;
else
@@ -67,7 +67,7 @@ UI.With = function (argFunc, contentBlock) {
};
block.init = function () {
this.data = UI.emboxValue(argFunc, safeEquals);
this.data = UI.emboxValue(argFunc, UI.safeEquals);
};
block.materialized = function () {