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
Remove Styles from Text When Copying/Cutting Using CSS or JavaScript
How to Detect When an HTML Element's Class Changes
Highlight an Individual Word Within a Text Block on Hover
Why Are Initial CSS Styles Not Visible on Dom Element.Style Field
How to Save an Image with CSS Filter Applied
How to Override the Style of the Material-Ui Switch Component When Checked
Bootstrap Progress Bar Progression
How to Width-Wise Shrink-Wrap Content That Spans More Than One Line
JavaScript Not Working in Android Webview
How to Remove an Existing Class Name and Add a New One with Jquery and Cookies
Onclick JavaScript Function Working Only on Second Click
How to Show a Label Beyond a Certain Zoom Level in Leaflet
Jquery Calendar Time Selection Suggestion