qbolec
qbolec

Reputation: 5134

How to force Meteor's Blaze to batch changes applied to #each block

I have a template which is mostly equivalent to:

<div class="line">
{{#each part in line}}
  <span class="part">{part}</span>
{{/each}}
</div>

where the line: string[] is a partition of a single line into one or more parts. The problem is that when the content of line changes, the template tries to match old elements with new elements (which is fine) and applies changes one by one (which is not fine) to the DOM. In particular if the old value of line was ["Hello","world"] and the new one is ["Hello world"], then there is a short period of time when the user is presented new value of line[0] combined with old value of line[1], which is ["Hello world","world"]. Most of the time this gets unnoticed, but in case when the parts are long enough compared to the screen width, it might happen that <span class="part">Hello world</span><span class="part">world</span> does not fit into a single line, which in turn causes the whole further content to be moved one line lower, only to be later moved again one line higher when line[1] gets finally removed.

One solution which I currently use is to replace the whole #each loop with custom helper {{{ helpMeRenderThisLine line }}} which builds the HTML string manually, but obviously this violates separation of concerns and my code style.

I'm new to Meteor, but these are the directions I would like to investigate:

Upvotes: 0

Views: 65

Answers (1)

Billybobbonnet
Billybobbonnet

Reputation: 3226

Is there a way to render a loop non-reactively? I've heard there was something like #constant block, but it is no longer supported.

It is pretty straightforward to make a part of the the DOM non reactive: you just need to use a non reactive helper. E.g. in you template rendered or created function, you can attach your string array to your template instance (not a cursor using this.myStrings = Collection.find() but an array using this.myStrings = Collection.find().fetch(), this being your template instance).

You then return it in your helper using return Template.instance().myStrings

Is there a way to "batch" the whole updating process into a single DOM reflow?

Actually, it depends on how you refresh the original data. The above solution should do that. Keep in mind that you can nest templates to control the granularity of the DOM refreshes. Also make sure you control your subscriptions in case they are overlapping with the same published content. You can wait for every one to be ready using Template.subscribtionReady() (template level) or subscribtion.ready() (sub level)

Is there a way to use the momentum package to remove the flicker?

The flicker should go away if you refresh properly the data.

Is there a way to use a smaller Blaze template to render the single line in non-reactive fashion?

Not sure about what you ask here but a good practice would be to try to make component level templates, it might help in fine tuning the DOM rendering.

<div class="line">
{{#each part in line}}
  {{> Part}}
{{/each}}
</div>

Upvotes: 0

Related Questions