Meteor ReactiveVar not updating template


#1

I have build an app with meteor 1.8 in webstorm, using default todos. App goes like that

/imports/api/TestCollection.js:’

import { Mongo } from 'meteor/mongo';
import { Factory } from 'meteor/dburles:factory';
import SimpleSchema from 'simpl-schema';


class TestsCollection extends Mongo.Collection {
    constructor(collectionName){
        super(collectionName);
        console.log('created');
    }
    insert(doc, callback) {
        const ourDoc = doc;
        ourDoc.testField = new Buffer([0,1,2]);
        const result = super.insert(ourDoc, callback);
        return result;
    }
}

export const Tests = new TestsCollection ('tests');

Tests.schema = new SimpleSchema({

    name: {
        type: String,
        max: 512,
        required: true,
    },
    text: {
        type: String,
        max: 512,
        required: true
    },
    testField: {
        type: Buffer,
        max: 512,
        required: true
    }

});

Tests.attachSchema(Tests.schema);

/imports/api/server/publication.js:

import { Meteor } from 'meteor/meteor';

import { Tests } from '../tests';

Meteor.publish('tests', function () {
    return Tests.find({});
});

So it is my collection, now i want to make it reactive… and display a spinner when data loads, so i add simple package sacha:spin, which display spinner using {{> spinner }} here is html:

/imports/ui/pages/test_Tmp.html

<template name='test_Tmp'>
<h3>This is test page #1</h3>
<table>
  <tr>
     <th>name</th>
     <th>info</th>
  </tr>
  {{#unless testsReady}}
     {{ >spinner }}
  {{else}}
  {{#each getTests}}
  <tr>
      <td>{{name}}</td>
      <td>{{text}}</td>
  </tr>
  {{/each}}
 {{/unless}}
</table>
</template>

And js part
/imports/ui/pages/test_Tmp.js:

import './test_Tmp.html'
import { Mongo } from "meteor/mongo";


const Tests = new Mongo.Collection('tests');

Template.test_Tmp.onCreated(function() {
    this.testsReady = new ReactiveVar(false);
    var self = this;
    var sub = this.subscribe('tests', function(){
         if(sub.ready()) {
                self.testsReady.set(true);
                alert('testsReady is TRUE');
            }
   });
    
});

Template.test_Tmp.onRendered(function onRendered() {
   //do some stuff


});

Template.test_Tmp.helpers({
 
    testsReady(){
        return Template.instance().testsReady.get();
    },  
    getTests(){
        var data = Tests.find({});
        console.log(data);
        return data;

    }
});

The problem is, this doesn’t work!! testsReady updates to TRUE, when subscription is ready, alert windows is displayed, but template is not rerendered, it displays spinning spinner, unless i click on another link, so flow router will render another template in the same layout, and when i come back, i see data from collection displayed. Why reactive var doesn’t work and template not getting updated?

Here is my routes.js, which are imported only in client.js:

import { FlowRouter } from 'meteor/kadira:flow-router';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';

import '../../ui/layouts/my_Layout'
import '../../ui/pages/funny_Tmp'
import '../../ui/pages/test_Tmp'


FlowRouter.route('/stuff', {
    name: 'funny.stuff',
    action: function(params) {
        BlazeLayout.render("my_Layout", {main: "funny_Tmp"});
    }
});

FlowRouter.route('/test', {
    name: 'funny.test',
    action: function(params) {
        BlazeLayout.render("my_Layout", {main: "test_Tmp"});
    }
});

Can someone help? I stuck forever on this simple thing, maybe it has something to do with my modules? I have created my meteor app using todos template in webstorm 2019


#2

I believe you can get rid of the var sub = and just have the this.subscribe. Then on the helper just return Template.instance(). subscriptionsReady()


#3

I don’t typically use a reactive var to drive a spinner like that in onCreated

Typically I use an isReady() template helper that simply returns FlowRouter.subsReady() - you can even pass in a specific subscription name if you want

Or just use Template.SubscriptionsReady


#4

I tried what you suggested still same result, i changed like that:

Template.test_Tmp.onCreated(function() {

    this.subscribe('tests');
    
});
Template.test_Tmp.helpers({
 
    testsReady(){
        var ready = Template.instance().subscriptionsReady();
        console.log(ready);
        return ready;
    },  
    getTests(){
        var data = Tests.find({});
        console.log(data);
        return data;

    }
});

Same effect, the helper testsReady called only once, and no more for some reason, maybe sometthing to do with flow router or something like that?

Here is output of my packages:

meteor list
aldeed:collection2-core  2.1.2  Core package for aldeed:collection2
blaze-html-templates     1.1.2  Compile HTML templates into reactive UI with Meteor Blaze
dburles:factory          1.1.0  Factories for Meteor
ecmascript               0.12.6* Compiler plugin that supports ES2015+ in all .js files
es5-shim                 4.8.0  Shims and polyfills to improve ECMAScript 5 support
insecure                 1.0.7  (For prototyping only) Allow all database writes from the client
kadira:blaze-layout      2.3.0  Layout Manager for Blaze (works well with FlowRouter)
kadira:flow-router       2.12.1  Carefully Designed Client Side Router for Meteor
meteor-base              1.4.0  Packages that every Meteor app needs
mobile-experience        1.0.5  Packages for a great mobile user experience
momentjs:moment          2.24.0  Moment.js (official): parse, validate, manipulate, and display dates - official Meteor packaging
mongo                    1.6.2  Adaptor for using MongoDB and Minimongo over DDP
reactive-var             1.0.11  Reactive variable
sacha:spin               2.3.1  Simple spinner package for Meteor
shell-server             0.4.0  Server-side component of the `meteor shell` command.
standard-minifier-css    1.5.3  Standard css minifier used with Meteor apps by default.
standard-minifier-js     2.4.1  Standard javascript minifiers used with Meteor apps by default.
tracker                  1.2.0  Dependency tracker to allow reactive callbacks
twbs:bootstrap           3.3.6  The most popular front-end framework for developing responsive, mobile first projects on the web.

#5

I just tried to return FlowRouter.subsReady() instead of Template.instance().subscriptionsReady();, and FlowRouter.subsReady() returns true, i see that in console.log, so now spinner is not getting displayed, however, the collection doesn’t getting displayed either for some reason, the helper getTests doesn’t get called


#6

I assume you have a server side collection called tests, and a publication which exposes it?

Do you have any console errors in either the client or the server

If you inspect the frames of the websocket, do you see the subscription being issued and a “ready” response returned, or any of the data being returned, or an error?


#7

Yes i have a server side collection, please see my original post, under:

/imports/api/TestCollection.js: i posted the code which creates collection and publishes it. About inspecting frames i'm not sure how to do that. I have some css errors in merged stylesheet, and marked as my page urltests`
the errors are:

Error in parsing value for ‘background’.  Declaration dropped.
test:1:96
Expected ‘none’, URL, or filter function but found ‘progid’.  Error in parsing value for ‘filter’.  Declaration dropped.

This is the block which produces error:

<style type="text/css">.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}</style>

This is jquery sparkline produced stylesheet, it is most likely not the case, i tried removing sparklines from my layout. it didn’t help. Besides that i have some errors in bootstrap/merged stylesheet all css related.

I added some console log also to see what happens:

import './test_Tmp.html'
import { Mongo } from "meteor/mongo";


const Tests = new Mongo.Collection('tests');

Template.test_Tmp.onCreated(function() {
    this.testsReady = new ReactiveVar(false);
    var self = this;
    var sub = this.subscribe('tests');
    Tracker.autorun(function () {
            if(sub.ready()){
                self.testsReady.set(true);
                console.log('data is ready: ' + self.testsReady.get());
            }
    });
    
});


Template.test_Tmp.helpers({
 
    testsReady(){
        var result = Template.instance().testsReady.get();
        console.log('Helper, testsReady value: ' + result);
        return result;
    },  
    getTests(){
        console.log('Returnung collection');
        var data = Tests.find({});
        console.log(data);
        return data;

    }
});

I get the following messages in console after that:

Helper, testsReady value: false

data is ready: true

So as you can see i get a message from testReady helper, but only ONCE. After that i get a message from Tracker.autorun, with the result of reactive var, which is now true, so i should get testReady helper called a second time but no this doesn’t happen. Maybe it has to do with my browser? Im under debia, i tried from firefox/chrome same results


#8

Also i just tried to render from FlowRouter my template without layout, i removd layout and all css (except bootstrap ) from client/css, still i get same result


#9

Can you debug your code - and ensure that this in you onCreated is === to Template.instance() in your helper?

Also, in your helper - create a setInterval to log the value of testsReady.get() to see if it ever changes as far as the helper is concerned (this will check if reactivity is broken, or if something else is going on).

Regarding frames - in google chrome, in the network tab, if you find the websocket connection (you can filter by websocket) you can inspect each frame sent and received - the single best feature of google chrome, its saved me hours of debugging time a bunch of times


#10

Thanks, i have tried what you suggested i checked like this:

var global_check = 0;

Template.test_Tmp.onCreated(function() {
    this.testsReady = new ReactiveVar(false);
    var self = this;
    global_check = self; //added
    var sub = this.subscribe('tests');
    Tracker.autorun(function () {
            if(sub.ready()){
                self.testsReady.set(true);
                console.log('data is ready: ' + self.testsReady.get());
            }
    });
    
});

Template.test_Tmp.helpers({
 
    testsReady(){
         var self = Template.instance();
         setInterval(function() {
            console.log(self.testsReady.get());
        }, 1000);

       if(self === global_check)
           console.log("instances are equal");

        var result = self.testsReady.get();

        console.log('Helper, testsReady value: ' + result);
        return result;
    },  
    getTests(){
        console.log('Returnung collection');
        var data = Tests.find({});
        console.log(data);
        return data;

    }
});

So after this changed i get:
Instances are equal
And then a spam of

true
true

etc

It seems reactivity was broken


#11

do you have your code in a repository you could share? I’ve not seen something like this before - except when you have an error somewhere that stops tracker from rerunning - these are typically obvious as they get printed to the console as a stack trace


#12

Allright after 50+ hours i figure out… I had a script in client folder which i didnt remove, that was creating a steps component… when i commented the creation of steps component reactivity started working… Now, i have another question, why jquery steps breaks reactivity and how do i use it in meteor?

For example i have made a step:

Template.users.onRendered(function onRendered() {

    $("#users-creation").steps({
        headerTag: "h3",
        bodyTag: "section",
        transitionEffect: "slideLeft",
        autoFocus: true
    });

});

html:

  <div id="users-creation">
     <h3>Choose the role</h3>
        <section>
            <div class="roles-list">
            <tr>
                     <th>Role name</th>
                     <th>Description</th>

              </tr>
              {{#unless rolesIsReady}}
                      {{> spinner}}
               {{else}}
                     {{#each roles}}
                        <tr>
                                  <td>
                                           <h1>{{ this.name }}</h1>
                                  </td>
                                   <td>
                                             <p>{{ this.desc }}</p>
                                  </td>
                       </tr>
                     {{/each}}
               {{/unless}}
            </div>
       </section>
       <h3>Provide data</h3>
         <section>
            <!--next step code here -->
         </section>
   </div>

So, as you can see reactive content inside steps block not having reactivity, can someone help, how to solve this and use this plugin with reactive content in meteor?


#13

Yes the problem were elsewhere, the jquery-steps plugin was causing bad things


#14

steps probably messes with the DOM, so I doubt it is compatible with meteor reactivity


#15

is there something you can suggest to be compatible with reactive stuff?


#16

It depends what you are trying to do?


#17

i need to build app a user creation in some panel, which is gonna be admin panel for news site. I wanted to put it into this steps component so it looks beutiful like choose roole -> enter name -> enter password / email


#18

You can do the same with pure meteor - use a reactive var to track the step number


#19

i guees that what i have to do, i try to make animation etc myself


#20

I have found a solution, if anyone enctouner same problem look here

Basically you wrap the part that should be reactive in another template, and then on the parent template, after creating a steps component you trigger the rendring of your child template with reactive stuff, that way it works, really thanks to this man, thanks everyone for helping out the problem