Blaze: How to force a template to rerender from scratch?


#1

I’m using an image slide show based on the jQuery plugin ‘slick.js’. To use this, you set a DIV including some images and then call $(theDiv).slick() to initialize it. Slick then adds its interface elements to this DIV.

This works until the set of images is expanded reactively, i.e. if someone adds more images. In this case, Blaze adds the new images to the already modified DOM instead of re-rendering the whole template again. Although the .slick() method is called again, the slide show does not update because it thinks it has been already initialized.

So I’m asking myself if there is any way to tell Blaze to re-render a whole (sub-)template instead of adding new elements?


Isotope package with nested templates
#2
{{#if anything}}
  {{> subtemplate}}
{{/if}}

subtemplate will be re-rendered whenever anything changes from false to true.


#3

@Steve: Thanks for your fast reply. The problem I see with this approach is that I would have to set “anything” from true to false and back to true in quick succession, since the subtemplate should remain visible. The only approach for this that comes to my mind is to use a timer, but this seems a bit quirky. Isn’t there a programmatic approach to trigger a full re-rendering?


#4

I now tried the timer approach. But this doesn’t work: In order to toggle the ‘anything’, you have to use some ReactiveVar. This ReactiveVar must also be used (unset) in the autorun function that triggers it and then re-set in the timer function. But this re-set also triggers the autorun again, which causes an endless loop.


#5

Perhaps rerendering the entire template is not needed.
Are you using an observer to see if the cursor for your images changes? Are you using slick methods for adding/removing elements after initializing? Do you use a small timeout before firing the slick methods, after the cursor changes, to prevent slick from trying to apply the changes before they’ve had the time to be added to the DOM?


#6

@camiel: I do not know what you mean by an observer, but I’ve got a reactive cursor on the images list in the database, so that the template will re-render if the images list is extended.

Until now, I’m not using the add/remove-Methods of slick, as this this would require quite a lot of logic to determine the actual differences between the image lists. Or is there a way to easily detect in what respect a cursor has changed (i.e. what the actual differences are) when it is triggered due to a changed data set? This would be very useful in this case. Otherwise, I would have to run through the images array by hand to detect the changes.

BTW: I also tried to use Blaze.render() and Blaze.remove() to manually trigger a whole re-rendering of the subtemplate containing the image slider. However, the autorun where I placed it is run 3 times for every single update (don’t know why, since there is only one reactive var in the autorun), so I don’t this is a good solution, especially on slow mobile devices.


#7

http://docs.meteor.com/#/full/observe_changes


#8

@camiel: Cool, that sounds interesting! Thanks for the link.


#9

I’ve experimented with the observe-Approach, but this did not work either. Reason: The observer was fired before the new image elements had been added to the page. To workaround this, I would have to use Blaze.render() to render the template directly. I was hesitating to do that, since I did not want to risk memory holes if Blaze.remove() wasn’t called correctly.

Yet, I was able to find a much easier approach to my solution: I’m now using a special helper that is called in the loop that also constructs the images:

    {{#each images}}
    <div class="item-display__image">
      <img src="{{uri}}">
    </div>
    {{updateCarousel}}
    {{/each}}

This updateCarousel helper checks via jQuery if there is a DIV that hasn’t been processed by the slick logic so far (these are easily to detect since slick builds wrapper DIV’s around each processed element). And if updateCarousel finds one of these images, it calls slick’s add method. Here’s the helper:

  updateCarousel: function() {
    var newImages = $('.slick-carousel > .item-display__image');
    for (var i = 0; i < newImages.length; i++) {
      $('.slick-carousel').slick('slickAdd', newImages.get(i));
    }
  }

I wasn’t sure whether this would work since it relies on the DIV to be rendered to the DOM before the helper is being called, but luckily this is the case. The only downside to this approach is that this helper is called a lot of times (sometimes 14 times even if only one image has been added), and I have no clue why this is the case.


#10

The observer callback running before the elements have had a chance to get added to the DOM is why I suggested putting the slick methods in a small timeout. Make the timeout too small or none and it will fire too early, make it too big and the user will see an image in the wrong place just before slick corrects it. To get around this add the images to the DOM hidden, when the timed slick method runs also update the css to display the image.

Perhaps there are better ways of doing this but the above should work I’d imagine.


#11

@waldgeist this thread just save me on another issue that is very similar to this. Thanks for sharing!

https://forums.meteor.com/t/quill-editor-updating-existing-text/9107/7?source_topic_id=9059

#12

@aadams: You’re welcome, nice to hear that!


#13

@camiel: I wanted to avoid these timeouts, as this is quite unreliable - especially if you’re developing a mobile app that may run on a slow device. The solution I found now (using an update helper) works for me now. The only drawback is that Blaze runs this update very often, and I have no clue why, since there’s only one update of the underlying database cursor involved. Anyway, the frequent runs do not really matter as it is only the first line that’s actually time-consuming. The for-loop won’t run if there’s nothing to update.


#14

Using your approach, instead of having each helper adding all images you could in the template give the div an id that’s the images _id and give that _id as an argument to the helper. The helper can then just add only that image to the carousel.


#15

@camiel: That’s a good idea!


#16

I had this question and found an answer.

I am using BlazeLayout so I was able to do this:

BlazeLayout.reset() // this will remove the current template.
BlazeLayout.render(...) // rerender

I was inspired to look for this given this stackOverflow answer.


Force entire template to re-render on data change