Meteor + Materialize: collapsable in for each does

2019-02-11 02:57发布

问题:

I have a collapsable (materialize) whose elements are created from a for each, however the "dropdown" doesn't work. Everything that isn't in the for each works.

How can I fix this problem?

jobList.html

<template name="jobList">
<ul class="collapsible" data-collapsible="accordion">
    {{#each jobs}}
        <li>
            <div class="collapsible-header">{{title}}</div>
            <div class="collapsible-body"><p>{{description}}</p></div>
        </li>
    {{/each}}
</ul>

jobList.js

Template.jobList.rendered = function () {
    $('.collapsible').collapsible({
        accordion: false
    });
};

Template.jobList.helpers({
    jobs: function() {
        return Jobs.find();
    }
});

The template jobList is in another template that does nothing appart from having {{> jobList}}.

回答1:

This problem is relative to DOM readiness, at the time you're executing the jQuery plugin initialization, the {{#each}} loop has not yet rendered HTML elements.

What you can do to solve this problem is defining a separate function to return the cursor you want to iterate over, and observe this cursor inside an autorun inside the onRendered callback of your template.

When we detect the cursor count is modified, it means a document has been added (in particular when the subscription is ready and the initial set of documents made their way to the client) or removed, and we have to re-run the jQuery plugin initialization.

It's important to wait for every other current invalidated computations to finish before running the jQuery initialization, this is why we need to use Tracker.afterFlush (we can't predict in which order invalidated computations are re-run, we can only execute code after this process is done).

That's because the helper returning our cursor is a computation too and will be invalidated when a document is added or removed, thus inserting or removing the corresponding DOM subset : it is then vital to execute our jQuery plugin initialization after DOM manipulation is done.

function jobsCursor(){
  return Jobs.find();
}

Template.jobsList.onRendered(function(){
  this.autorun(function(){
    // registers a dependency on the number of documents returned by the cursor
    var jobsCount = jobsCursor().count();
    // this will log 0 at first, then after the jobs publication is ready
    // it will log the total number of documents published
    console.log(jobsCount);
    // initialize the plugin only when Blaze is done with DOM manipulation
    Tracker.afterFlush(function(){
      this.$(".collapsible").collapsible({
        accordion: false
      });
    }.bind(this));
  }.bind(this));
});

Template.jobsList.helpers({
  jobs:jobsCursor
});