I'm new to Meteor/Blaze but that is what my company is using.
I'm struggling to understand how Blaze decides to render what based on ReactiveDict
TL;DR
I create some children templates from a ReactiveDict array in the parent array. The data is not refreshed in the children templates when the ReactiveDict changes and I don't understand why.
I probably have misunderstood something about Blaze rendering. Could you help me out?
Parent
Parent Html template
<template name="parent">
{{#each child in getChildren}}
{{> childTemplate (childArgs child)}}
{{/each}}
</template>
Parent Javascript
Reactive variableThe template renders children templates from a getChildren helper that just retrieves a ReactiveDict.
// Child data object
const child = () => ({
id: uuid.v4(),
value: ""
});
// Create a reactive dictionary
Template.parent.onCreated(function() {
this.state = new ReactiveDict();
this.state.setDefault({ children: [child()] });
});
// Return the children from the reactive dictionary
Template.parent.helpers({
getChildren() {
return Template.instance().state.get('children');
}
});
Child template arguments (from parent template)
The parent template gives the child template some data used to set default values and callbacks.
Each is instantiated using a childArgs function that uses the child's id to set the correct data and callbacks.
When clicking a add button, it adds a child to the children array in the ReactiveDict.
When clicking a delete button, it removes the child from the children array in the ReactiveDict.
Template.parent.helpers({
// Set the children arguments: default values and callbacks
childArgs(child) {
const instance = Template.instance();
const state = instance.state;
const children = state.get('children');
return {
id: child.id,
// Default values
value: child.value,
// Just adding a child to the reactive variable using callback
onAddClick() {
const newChildren = [...children, child()];
state.set('children', newChildren);
},
// Just deleting the child in the reactive variable in the callback
onDeleteClick(childId) {
const childIndex = children.findIndex(child => child.id === childId);
children.splice(childIndex, 1);
state.set('children', children);
}
}
}
})
Child
Child html template
The template displays the data from the parent and 2 buttons, add and delete.
<template name="child">
<div>{{value}}</div>
<button class="add_row" type="button">add</button>
<button class="delete_row" type="button">delete</button>
</template>
Child javascript (events)
The two functions called here are the callbacks passed as arguments from the parent template.
// The two functions are callbacks passed as parameters to the child template
Template.child.events({
'click .add_row'(event, templateInstance) {
templateInstance.data.onAddClick();
},
'click .delete_row'(event, templateInstance) {
templateInstance.data.onDeleteClick(templateInstance.data.id);
},
Problem
My problem is that when I delete a child (using a callback to set the ReactiveDict like the onAddClick() function), my data is not rendered correctly.
Text Example:
I add rows like this.
child 1 | value 1
child 2 | value 2
child 3 | value 3
When I delete the child 2, I get this:
child 1 | value 1
child 3 | value 2
And I want this:
child 1 | value 1
child 3 | value 3
I'm initialising the child with the data from childArgs in the Template.child.onRendered() function.
- Good: The
getChildren()function is called when deleting thechildin theReactiveDictand I have the correct data in the variable (childrenin theReactiveDict). - Good: If I have 3 children and I delete one, the parent template renders only 2 children.
- Bad: Yet the child's
onRendered()is never called (neither is the child'sonCreated()function). Which means the data displayed for the child template is wrong.
Picture example
I am adding pictures to help understand:
Correct htmlThe displayed HTML is correct: I had 3 children, and I deleted the second one. In my HTML, I can see that the two children that are displayed have the correct ID in their divs. Yet the displayed data is wrong.
Stale dataI already deleted the second child in the first picture. The children displayed should be the first and the third. In the console log, my data is correct. Red data is the first. Purple is the third.
Yet we can see that the deleted child's data is displayed (asd and asdasd). When deleting a tag, I can see the second child's ID in the log, though it should not exist anymore. The second child ID is in green.
I probably have misunderstood something. Could you help me out?


I fixed my problem. But I still don't understand how Blaze chooses to render. Now, the solution looks a bit like the one given by @Jankapunkt in the first part of his solution, but not exactly. The
findto get the child was working completely fine. But now that I make the template rendering dependent on a reactive helper, it re-renders the template when the id changes (which it did not when it was only dependent on the child itself from theeach...inloop).In the end, I don't understand what the
each...inloop does and how it uses the data to loop. See Caveats.To give credits where it's due, I had the idea of implementing that dependency from this post.
Edits from the original code
I edit the parent template to make the child rendering dependent on its own id. That way, when the
child.idchanges, the template re-renders.Html template
I added a dependency on the
child.idto re-render the child template.Javascript
I have now two helpers. One to return the ids for the
each...inloop, the other to return the child from the id and force the child template re-render.Complete Code
Here is the complete solution.
Parent
Html template
Javascript
Child
Html template
Javascript
Caveats
The solution is working. But my
each..inloop is weird.When I delete a child, I get the correct IDs when the
getChildrenIds()helper is called.But the
each..inloops over the original IDs, even those who were deleted and are NOT in thegetChildrenIds()return value. The template is not rendered of course because thegetChild(childId)throws an error (the child is deleted). The display is then correct.I don't understand that behaviour at all. Anybody knows what is happening here?
If anybody has the definitive answer, I would love to hear it.