Search code examples
meteormeteor-helperslick.jsflow-router

How do I use Slick.io carousel with meteor and collections?


I've come across kenwheeler's image carousel and I'm trying to make it work within my Meteor app. I'm using the risul:slick wrapper.

The problem is that the recommendation is to initialise the template on rendering but at that point the images in the subscription haven't necessarily all been loaded... if I use static images on a local drive everything works well, but if I try and create slides within an {{#each}} loop it all goes wrong.

I have tried using a {{#if Template.subscriptionsReady}} block and manually call the initialiser using a Template helper but that hasn't worked either.

The interesting thing I noticed when putting a console.log statement into the initialiser is that it seems to get called three times when I first navigate to the page but when I do a browser refresh it only gets called once and breaks (this happens with static images as well if I don't have the initialisation code within the onRender block?).


Template code with static references (working as long as the .slick() call is sitting within the Template.image.onRendered block):

<template name="image">
  <div id="carousel">
    <div id="demo-box"><img src="../../img/brush.jpg" /></div>
    <div id="demo-box"><img src="../../img/drill.jpg" /></div>
    <div id="demo-box"><img src="../../img/hammer.jpg" /></div>
    <div id="demo-box"><img src="../../img/shovel.jpg" /></div>
    <div id="demo-box"><img src="../../img/spanner.jpg" /></div>
    <div id="demo-box"><img src="../../img/tape.jpg" /></div>
  </div>
</template>

Reactive template

<template name="image2">
  {{#if Template.subscriptionsReady}}
    {{slickInit}}
    <div id="carousel">
      {{#each images}}
        <div id="demo-box"><img src="{{url}}" /></div>
      {{/each}}
    </div>
  {{else}}
    <div>Loading...</div>
  {{/if}}
</template>

Template.image.onCreated( () => {
  // limit of images returned = 5
  Template.instance().subscribe('images', 5);
});

Template.image.onRendered( () => {
  $('#carousel').slick({
    infinite: false,
    dots: true,
    slidesToShow: 1,
    slidesToScroll: 1,
    arrows: true
  });
});

Template.image.helpers({
  images() {
    return Images.find();
  },

  // this is supposed to replace the onRender call
  slickInit() {
    $('#carousel').slick({
      infinite: false,
      dots: true,
      slidesToShow: 1,
      slidesToScroll: 1,
      arrows: true
    });
  }
});

Solution

  • It works if each item of the carousel is handled within its own template which can then intialise slick onRendered.

    <template name="image">
      {{#if Template.subscriptionsReady}}
        <div id="carousel">
          {{#each images}}
            {{> imageItem}}
          {{/each}}
        </div>
        <button type="button" class="btn btn-default" id="addSlide">Add</button>
      {{else}}
        <div>Loading...</div>
      {{/if}}
    </template>
    
    <template name="imageItem">
      <div id="demo-box"><img src="{{url}}" /></div>
    </template>
    

    Template.imageItem.onRendered( () => {
      $('#carousel').slick({
        infinite: false,
        dots: true,
        slidesToShow: 1,
        slidesToScroll: 1,
        arrows: true
      });
    });