Meteor: Tracker.Autorun/Observechanges & Collections Not Working as Expected

Meteor: Tracker.autorun / observeChanges & collections not working as expected

that means that I am trying to access this document before the
collection has loaded

Seems like you get the problem, now lets get ride to some possible solutions.

Meteor version 1.1

If you are using the new meteor version 1.1 (you can check running meteor --version)

use this.

First on the onCreated function use this.

Template.progressBar.onCreated(function () {
var self = this;

self.autorun(function () {
self.subscribe("Progress");
});
});

See more about subscriptionReady on the DOCS.
Now on the HTML use like this.

<template name="progress">
{{#if Template.subscriptionsReady}}
<div id="progress-bar" style="width:{{curValue}}; background-color:*dynamicColor*;"></div>
{{else}}
{{> spinner}} <!-- or whatever you have to put on the loading -->
{{/if}}
</template>

Meteor under 1.0.4

You can have on the router something like a waitOn:function(){}

waitOn:function(){
Meteor.subscribe("Progress");
}

or since helper are asynchronous do something like this (not recommendable).

Template.progressBar.helpers({
curValue: function () {
query = Progress.findOne({user: Meteor.userId()}).curValue;
if(query != undefined){
return query;
}else{
console.log("collection isn't ready")
}
}
});

Meteor Tracker.autorun is not calling on every updates

If you want to use an autorun block, you need to put inside the autorun the reactive element that will trigger it. In your case, it would then be:

Tracker.autorun(function () {
var myCursor = Tasks.find().fetch();
console.log("On Load");
});}

Read this to learn more about reactivity mechanisms.To make it cleaner, you could also attach the autorun to your template (see the link)

Meteor Tracker not receiving subscriptions automatically

onCreated and onRendered aren't reactive, but template helpers are. So, to constantly detect changes in those two you'd have to create a reactive context and place it inside them which can be achieved using Tracker.autorun. Also, be advised that it's better to keep onRenderd for DOM manipulations and onCreated for subscriptions and creation of reactive variables.

You don't need double subscriptions to detect changes and IMO this is generally a bad practice.

Template.foo.onCreated(function onCreated() {
const self = this;
this.autorun(function() {
const id = FlowRouter.getParam('_id');
self.subscribe('obj', id);
});
});

Template.foo.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
// do whatever you want
$('#obj').addClass('highlight');
}
});

Meteor : autorun won't make my code dynamic

Try using nested templates for this. So that your wrapper template subscribes to data and only renders nested template when subscriptions is ready:

//wrapper template that manages subscription
Template.wrapper.onCreated(function() {
this.subscribe('allRuns');
});

Template.runs_show.onRendered(function() {
google_maps.KEY = "MY API KEY";
google_maps.load(function(google){
var map = new gmaps({
el: '#map-gmaps',
lat: -12.043333,
lng: -77.028333
});

Tracker.autorun(function() {
// Run.find will re-trigger this whole autorun block
// if any of the elements of the collection gets changed or
// element gets created or deleted from collection
// thus making this block reactive to data changes
// this will also work with findOne in case you only want to
// one run only
var runs = Run.find({"maxSpeed" : "75"}).fetch();
// map.clear() or something like that to remove all polylines
// before re-rendering them
runs.forEach(function(run){
map.drawPolyline({
path : path,
strokeColor : '#FC291C',
strokeOpacity : 0.85,
strokeWeight : 6
});
});

});
// ^^ this is pretty dumb and re-renders all items every time
// something more intelligent will only re-render actually
// changed items and don't touch those who didn't change but
// that will require a slightly more complex logic

});
});

wrapper.html:

<template name="wrapper">
{{#if Template.subscriptionsReady }}
{{> runs_show }}
{{/if}}
</template>

P.S. this is mostly pseudo code since I never tested it, so just use it as a guide

Meteor.subscribe is skipped, doesn’t work in Tracker.autorun

If you have a close look at the docs for subscribe, you'll find this note in a section about reactive computations:

However, if the next iteration of your run function subscribes to the same record set (same name and parameters), Meteor is smart enough to skip a wasteful unsubscribe/resubscribe.

So because you are always calling subscribe with the same arguments, meteor isn't actually restarting it. The trick is just to pass extra parameters to defeat this "optimization". For example:

Tracker.autorun(function() {
var user = Meteor.user();
var list = user && user.list;
if (!_.isEmpty(list)) {
Meteor.subscribe('Lists', list, function() {
console.log(Lists.find().count());
});
}
});

Here we are extracting the list variable from the user (assuming it's published) and using it as an extra parameter to force the subscription to rerun. If it isn't published, you could just use a random id like this:

Tracker.autorun(function() {
var user = Meteor.user();
Meteor.subscribe('Lists', Random.id(), function() {
console.log(Lists.find().count());
});
});

This should also work but may be a little less efficient because it will fire whenever any property of the user changes.

Meteor observeChanges(). How to check the actual changes?

Thanks @Season for the hint!


observeChanges only gives the new values, so you have to use observe, since it returns both the new and old documents. Then you need to compare them to see what exactly got changed. (See docs for observe on meteor.com)

Trades.find().observe({
changed: function(newDocument, oldDocument) {
// compare newDocument to oldDocument to find out what has changed
}
});

Can I specify what session variables a Tracker.autorun() function depends on?

As it turns out, this question has already been answered before.

The answer is to use Tracker.nonreactive(). The fixed code from my question would be:

Tracker.autorun(function() {
var foo = Session.get("foo")

var bar = Tracker.nonreactive(func() {
return Session.get("bar")
})

if (bar)
console.log("foo changed and bar is set")
else
console.log("foo changed and bar is not set")
}

Meteor Reactive Session: Not Working (Why?)

Finally figured it out. Apparently Session creates a string, so that Session.set('limit', 1) sets the limit to "1". Of course, strings can be processed in a Mongo collection request.

The solution was using {limit: parseInt(Session.get('limit')}.

Tracker.autorun only runs once

Looking at how tracker-react implements it, I changed my code like so

class FooComponent extends Component {

constructor(...args) {
super(...args);

this.state = {
model: this.getModel()
};
}

componentWillUnmount() {
this._unmounted = true;
this._modelComputation && this._modelComputation.stop();
super.componentWillUnmount && super.componentWillUnmount();
}

getModel() {
const model = {};

this._modelComputation && this._modelComputation.stop();

this._modelComputation = Tracker.nonreactive(() => {
return Tracker.autorun((computation) => {
const { id } = this.props;
const data = id && Collection.findOne(id);

if (data) {
Object.assign(model, data);
!this._unmounted && this.forceUpdate();
}
});
});

return model;
}

...

}

I don't fully understand why, but it works, now.



Related Topics



Leave a reply



Submit