Blaze inconsistency


#1

I’m seeing some very odd behaviour with blaze…

I have a template a little like this:

Template.mine.helpers({
  dummy() {
     //either of these
      Template.instance();
      console.log(this.items.length);
  }
});
Template.mine.onRendered(function() {
    const self = this;
    self.$("something").sortable({
     ...
     stop() {
      console.log(self.data.items);
     }
     ...
    })
});
<template name="mine">
  {{dummy}}
</template>

The workflow is a little complicated here, but essentially, an item is being added to the sortable table after the component is rendered. When I reorder the items, I get a different result in the stop function depending on whether the dummy helper is called. If it is called, the console.log in stop returns the correct number. If it is NOT called, the console.log returns whatever the component was initialized to.

Has anyone else noted this behaviour, and is it expected or a bug?


#2

I can assure you, that the sortable (npm package) is running fine with Blaze. I am using it in even some complex setups. The cause of the error is possibly in your code.

Some takeaways:

  • onRendered has also an autorun and can thus run based on reactive data, which is why you can (and should!) omit the dummy helper.
  • you can atach an internal flag to the self in order to prevent restarting sortable on each render cycle.
  • in onEnd you should use jquery again to fetch the items

Consider the following template:

<template name="mine">
  {{#if loadComplete}}
  <div id="something">
    {{#each items}}
      <div class="somethings-children" data-id="{{this._id}}">{{this.title}}</div>
    {{/each}}
  </div>
  {{/if}}
</template>

The corresponding sortable code that works even when adding new items

Template.mine.onRendered(function() {
    const self = this;
    self.autorun(() => {
      // assuming we created a ReactveDict named 'state'  in onCreated
      // and attached it to the current instance, we can check if our data
      // has initially been loaded and then continue to attach the sortable
      // we also know that the template has already been rendered
      const complete = self.state.get('loadComplete')
      if (complete && !self._sortableCreated) {
        self.$("#something").sortable({
          ...
        stop() {
         const items = []
          self.$("somethings-children").each(function (index, element) {
            items.push(instance.$(this).data('id'))
          })
          // do whatever you want with those items
          console.log(items)
        }
        ...
        })
        self._sortableCreated = true
      }) 
});

By doing so you will decouple the sortable logic from your data in the way, that it only relies on the event and the current DOM structure to get the items, not matter what state your Template internally consists of.


#3

Thanks, I’ll certtainly review this more as sortable reactive tables is an area we are weak in.

I’ll agree - sortable does work fine with blaze, we’ve been using our code (sans dummy helper) for over a year, with no issues. I can only assume that in the last few days some package got updated which broke this.

In any case, my point was not about sortable - I actually suspect that the same would be true if I used setInterval or similar. Why should a helper, which does absolutely nothing, impact the result of self.data? Ignore the self.data.items the items part isn’t relevant, its ANY property of self.data


#4

I see. I can only imagine that the helper causes a lot of invalidating and induces some tracker computations etc.

It’s a hard one but you may actually stuff your code with lots of debugger statements and check for any computations.