I am writing a custom Blaze block helper with children:
<template name="parent">
{{> Template.contentBlock ..}}
</template>
<template name="child">
{{> Template.contentBlock ..}}
</template>
My intended use case would be to have a Template with arbitrary child nodes, that I define in the html file.
{{#parent}}
{{#child id="child1" title="Child 1"}}
<p>This is content of child 1</p>
{{/child}}
{{#child id="child2" title="Child 2"}}
<p>This is content of child 2</p>
{{/child}}
{{#child id="childN" title="Child N"}}
<p>This is content of child N</p>
{{/child}}
{{/parent}}
No problem so far. However, in the parent Template's onCreated
/ autorun
I want to have access to child
templates. I want to use this data to dynamically create in the parent Template elements, based
Template.parent.onCreated(function () {
const instance = this;
instance.state = new ReactiveDict();
instance.autorun(function () {
const contentBlocks = // how?
instance.state.set("children", contentBlocks);
});
});
Template.parent.helpers({
children() {
return Template.instance().state.get("children");
}
});
Where children
would be used in the parent
template as following:
{{#parent}}
{{#each children}}
do something with {{this.value}}
{{/each}}
{{#child id="child1" title="Child 1"}}
<p>This is content of child 1</p>
{{/child}}
{{#child id="child2" title="Child 2"}}
<p>This is content of child 2</p>
{{/child}}
{{#child id="childN" title="Child N"}}
<p>This is content of child N</p>
{{/child}}
{{/parent}}
What I don't want is to access the contentBlock's content (the <p>
) but rather get a list of the added child
Templates.
Is that possible with the current Template / Blaze API? The documentation is a bit thin on that point.
It is basically the opposite of this post: How to get the parent template instance (of the current template)
Edit 1: Use parent View's Renderfunction (only partially working)
I found a way to get the parent
Template's children but not their data
reactively:
// in Template.parant.onCreated -> autorun
const children = instance.view.templateContentBlock.renderFunction()
.filter(child => typeof child === 'object')
.map(el => Blaze.getData(el._render()));
console.log(children);
// null, null, null because Blaze.getData(view) does return null
Another approach I found is to used a shared ReactiveVar
but both seem to me not clean enough. I just want to get the list of Template instances in the parent's js code.
Edit 2: Use a shared ReactiveVar (only partially working)
It is possible to use a shared ReactiveVar
as long as it is in the scope of both Templates:
const _cache = new ReactiveVar({});
Template.parent.onCreated(function () {
const instance = this;
instance.state = new ReactiveDict();
instance.autorun(function () {
const children = Object.values(_cache.get());
instance.state.set("children", children);
});
});
Template.parent.helpers({
children() {
return Template.instance().state.get("children");
}
});
Working (but only rendered once, not reactive):
Template.child.onCreated(function () {
const instance = this;
const data = Template.currentData();
const cache = _cache.get();
cache[data.id] = data;
_cache.set(cache);
});
Not working (child autorun is setting values, but new values are not rendered):
Template.child.onCreated(function () {
const instance = this;
instance.autorun(function() {
const instance = this;
const data = Template.currentData();
const cache = _cache.get();
cache[data.id] = data;
_cache.set(cache);
});
});