Skip to content

#each re-rendered with stale data context #468

Description

@josherich

Tested in Meteor 3.0.1
MacOS 14.6.1
expected behavior: data context in view should be the same as currentData while re-rendering
actual behavior: data context in view is stale while re-rendering

Here's the minimal reprod using Blaze test code:
In the test code, the final buf shows

[
    "getMsgs called: foo",
    "view:foo, data:foo",
    "---flush---",
    "getMsgs called: bar",
    "view:foo, data:bar", <------------- foo in view, but bar in currentData
    "view:bar, data:bar"
]

I'm not 100% sure if this is a bug or expected behavior, but it can cause confusion since it's a very common pattern.

Blaze.Template.registerHelper('$tpl', function() {
    return Blaze.Template.instance();
});
const R = ReactiveVar('foo');
const buf = [];

const eachTest = Blaze.Template('eachTest', function() {
    var view = this;
    return Blaze._TemplateWith(function() {
        return {
            foo: Spacebars.call(Spacebars.dot(view.lookup("$tpl"), "foo", "get"))
        };
    }, function() {
        return Spacebars.include(view.lookupTemplate("eachTestChild"));
    });
});
eachTest.onCreated(function() {
    const tpl = this;
    Object.assign(tpl, {
        foo: R,
    });
});

const eachTestChild = Blaze.Template('eachTestChild', function() {
    var view = this;
    return Blaze.Each(function() {
        return Spacebars.call(Spacebars.dot(view.lookup("$tpl"), "getMsgs"));
    }, function() {
        return ["\n    ", HTML.DIV("\n      Msg ", Blaze.View("lookup:msg", function() {
            return Spacebars.mustache(view.lookup("msg"));
        }), "\n      ", Blaze.View("lookup:$tpl.log", function() {
            return Spacebars.mustache(Spacebars.dot(view.lookup("$tpl"), "log"), view.lookup("msg"));
        }), "\n    "), "\n  "];
    });
});
Blaze.Template['eachTestChild'] = eachTestChild;
eachTestChild.onCreated(function() {
    const tpl = this;
    Object.assign(tpl, {
        getMsgs() {
            const foo = Template.currentData().foo;
            buf.push('getMsgs called: ' + foo);
            if (foo === 'foo') {
                return [{_id: '1', msg: 'foo'}];
            } else {
                return [{_id: '2', msg: 'bar'}];
            }
        },
        log(foo) {
            buf.push('view:' + foo + ', data:' + tpl.data.foo);
        }
    });
});

const div = document.createElement("DIV");
Blaze.render(eachTest, div);

R.set('bar');

buf.push('---flush---');
Tracker.flush();
console.log(buf);

Here's the app code

<template name="eachTest">
  {{> eachTestChild foo=$tpl.foo.get }}
</template>

<template name="eachTestChild">
  {{#each $tpl.getMsgs}}
    <div>
      Msg {{ msg }}
      {{$tpl.log msg }}
    </div>
  {{/each}}
</template>
Template.eachTest.onCreated(function() {
  const tpl = this;
  Object.assign(tpl, {
    foo: new ReactiveVar('foo')
  });
});

Template.eachTestChild.onCreated(function() {
  const tpl = this;
  Object.assign(tpl, {
    getMsgs() {
      const foo = Template.currentData().foo;
      console.log('get msgs', foo);
      if (foo === 'foo') {
        return [ { _id: '1', msg: 'foo' }];
      } else {
        return [ { _id: '2', msg: 'bar' }];
      }
    },
    log(foo) { console.log('view:', foo, ', data:', tpl.data.foo); },
  });
});

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions