ref: http://jsfiddle.net/ffmqd0zz/
html:
<div id='books_container'/>
<script type='ractive/template' id="books">
{{#books}}
<div decorator='decoration' id="{{id}}">
<span> title: {{ title }}</span>
<span> id: {{ id }}</span>
</div>
{{/books}}
</script>
javascript:
var MyDecorator = function(node) {
console.log("init MyDecorator for id: " + $(node).attr('id'));
return { teardown: function() {}};
};
var books = new Ractive({
el: '#books_container',
template: '#books',
data: {books: [{"id": 1174,"title": "franny and zoey"},
{"id": 1175,"title": "moby duck"}]},
decorators: { decoration: MyDecorator }
});
console.log("init complete")
books.set('books', [{"id": 1176,"title": "life, the universe and stuff"},
{"id": 1177,"title": "yellowbrick road"},
{"id": 1178,"title": "grapes of wrath"} ]);
notice that decorator initialization is logged to the console.
When Ractive is initialized, the two initial items in the "books" list are correctly decorated, as indicated by console.log messages. However when the list is changed with "books.set()", Ractive only decorates the third item on the list, and the decorators for books with id 1176 and 1177 are not initialized.
It seems to be optimizing performance by just initializing the decorator on the third, added, item, skipping the first two, even though they've been replaced.
Am I doing it wrong, or is this a bug? And can someone suggest a workaround, please.
thanks in advance
Ractive will only update things that change. With lists of nodes ({{each}}
) there are two strategies used for updates:
- Explicit array modifications like push or splice (or merge) produce identical DOM node modifications - meaning DOM nodes are left intact if not modified, otherwise removed or inserted.
- Setting the array reuses the DOM nodes, but even in this case only things that have changed will be updated with the structure of each list item.
So for decorators, if they have no parameters Ractive will not update them. One each way to change this to add a parameter to your decorator:
<div decorator='decoration:{{id}}' id="{{id}}">
Now the decorator will be torn down and reinitialized if the data it depends on changes ( see http://jsfiddle.net/ffmqd0zz/2/ ).
Somehow the docs have not been update, BUT you can also add a update
method to your decorator return parameter that will be called in lieu of the decorator being torndown on parameter value change:
var MyDecorator = function(node, title) {
console.log('new decorator', title);
function tooltip( title ) {
node.title = title;
}
return {
teardown: function() {},
update: function( title ) {
tooltip(title);
}
};
};
see http://jsfiddle.net/ffmqd0zz/3/
As a workaround you can pop and push the array elements one at a time. See full example. Relevant code:
books.pop('books');
books.pop('books');
books.push('books', {"id": 1176,"title": "life, the universe and stuff"});
books.push('books', {"id": 1177,"title": "yellowbrick road"});
books.push('books', {"id": 1178,"title": "grapes of wrath"});
I'm not sure if the behavior you saw is due to a bug or not. Like you said, it may be Ractive optimizing performance. One telling tidbit from the Ractive docs: "Directives are just instructions to Ractive - they don't actually get added to the element as attributes."