Meteor: why autorun executed twice in onCreated - meteor

When I declare autorun subscribe in the Template.onCreated function,
the autorun was fired twice so the publication is triggered twice as well.
Route.route 'moneyDepositRequest',
onRun: ->
console.log 'onRun'
Session.set 'condition', where: name: 'example'
Template.moneyDepositRequest.onCreated ->
console.log 'onCreated'
#autorun ->
console.log 'autorun'
#subscribe 'data', Session.get('condition')
if Meteor.isServer
Meteor.publish 'data', (condition) ->
console.log 'data publication'
Data.find()
This code shows this sequence of logs.
'data publication'
'onRun'
'onCreated'
'autorun'
'data publication'
I don't understand why 'data publication' is fired in advance before onCreated executed.
If I declared this autorun action in the onRenderd function, the problem is solved.
How could I understand this?

Tracker.autorun always executes the code right away, and then later whenever any reactive variables or functions within it change. So it's running once right away, and then immediately again when the route is triggered. Try this instead:
Template.moneyDepositRequest.onCreated ->
console.log 'onCreated'
#autorun =>
console.log 'autorun'
if !!Session.get('condition')
#subscribe 'data', Session.get('condition')
BTW, as a side note, I start moving away from CoffeeScript. ES6 will slowly quickly become the new standard, and CoffeeScript has no roadmap for ES6 transpilation.

I've found my huge mistake finally.
I've set the Session.set 'condition' on the other route using same template, so the Reactive was fired by that new Session condition before the route moved.
And there's one more thing.
If you use IronRouter and use template for several routes, you need to declare template: 'templateName' for every route's map.
For example just suppose that we have three router using same template.
Router.route 'first'
Router.route 'second',
template: 'sharedTemplate'
Router.route 'third',
template: 'sharedTemplate'
In this case, the first route will be fired with onCreated template hooks so the autorun and subscription will be fired again. (for the other templates, the screens are changed for data changes only by reactive subscription.
So if we declare template: 'sharedTemplate' on the first route,
it will work as we expected. (only changed by data when route was changed).
So all the happening was my stupid confusion, but the reactive is complex enough still if we want to use flexibly.

Related

What the proper way to avoid console warning, when meteor collection is not ready

Every time I refresh the page I receive the following console warning for every single helper that is returning something to template from collection. I know the reason is because the subscription is not ready yet, but what is the solution?
Exception in template helper: TypeError: Cannot read property 'x' of undefined.
I'm already using if(collection.find({}) !== undefined) , but this makes my codes so messy, there must be a way to fix this issue. then I tried guards and still not 100% solved.
In addition to Brendan's answer, using Blaze you can check if the subscriptions for the template is ready using
this.subscriptionsReady()
Which checks all the subscriptions scoped to the template with
this.subscribe()
in your onCreated or onRendered blocks
Meteor.subscribe returns a handle with a reactive method called .ready(). You can use that in your helper to only return the mongo cursor once it's ready.
Edit: docs

Why would iron-router ignore waitOn?

onAfterAction is run twice, once before the data arrives, and once after. Why is it run before the data arrives? Also, in this basic version, rendered is called after the data arrives, but in my app, it's called before the data arrives. Any idea why that might be? Basic reproduction:
https://github.com/lorensr/waiton-bug
Items = new Meteor.Collection 'items'
Router.configure
waitOn: ->
Meteor.subscribe 'items'
if Meteor.isServer
Meteor.publish 'items', ->
Items.find {}
Router.route '/',
name: 'hello'
You don't have a loadingTemplate defined. Iron Router can't use the loading template if you don't have one so the effect is the waiting effect of waitOn is ignored.
Simply add the loadingTemplate and it should work.
The onAfterAction is run once and after. The first when its waiting, the other times when there is a reactive change or the data is ready. If you want something that doesn't do this use onRun instead.
You are telling the Router to what for a collection subscription. The collection and the subscription both are reactive data sources. So when that collection changes, the waitOn fill fire and update the route including the onAfterAction.

What triggers an onAfterAction in IronRouter other than a route change?

I'm having trouble debugging an onAfterAction that I don't want to run. It happens when I click a certain div. Router.go is not being called (verified with debugger; in the iron router code), and the URL is not changing. I can't find anything in my click handlers that would cause a route change. The onAfterAction happens from a deps invalidation:
Router.configure.onAfterAction (routes.coffee:5)
RouteController.runHooks (route_controller.js:155)
(anonymous function) (route_controller.js:291)
RouteController.runHooks (route_controller.js:158)
(anonymous function) (route_controller.js:283)
Deps.Computation._compute (deps.js:196)
Deps.Computation._recompute (deps.js:210)
Deps.flush (deps.js:304)
Probably you have some reactive data source in onAfterAction (cursor, Session object).
It will rerun every time reactive data source will be changed.
You can forbid this behavior by wrapping function in onAfterAction :
Tracker.nonreactive(function(){})
Clicking the div caused the User doc to change. There was global onBeforeAction that checked to see if the user was logged in (if Meteor.user()), which is reactive and triggered a rerun of action and onAfterAction. I changed it to if Meteor.userId(), which did not get invalidated when updating the user doc.

Correctly stopping an autorun() function when template is destroyed

I have a template that contains a chart, rendered using MorrisJS. The chart should update when the currentData session variable is changed, so I have made it a reactive data source with:
Template.chart.rendered = function() {
var template = this;
Deps.autorun(function(c) {
// Stop if the template is removed from the dom
// Q: Is this really right?
if(template.__component__.dom.parentNode() === null) {
c.stop();
return;
}
var results = Session.get('currentData');
// ... render a chart with `results` as the data
Morris.Bar({element: template.$(".chart-container"), data: results, ...});
});
};
Notice how I do a fairly horrid check for when to stop the autorun above. This was necessary because without this, when I navigate away from the page using the template (I'm using iron-router) to another page and back, I get warnings in the log like "Can't select in removed DomRange". I'm pretty sure this is happening because the template instance is removed, but the autorun is still running.
I feel like I'm doing something wrong here, though. Is there (a) a better place to put the autorun so that it doesn't have this problem or (b) a better way to stop the computation when the template instance is removed from the DOM?
I tried to find a way to do it with created and destroyed handlers, but I couldn't figure out how.
Tracker.autorun returns a handle that you can store as a template instance property, then call its stop method in the onDestroyed lifecycle event.
Template.chart.onRendered(function(){
this.computation = Tracker.autorun(function(){...});
});
Template.chart.onDestroyed(function(){
this.computation.stop();
});
EDIT 29-09-2014
In newer versions of Meteor (0.9 onward), there is a new autorun function available on template instances which provide simpler code to achieve the same result : no need to store and stop the computation manually, this is taken care of by the framework.
Template.chart.onRendered(function(){
this.autorun(function(){...});
});
With the new autorun
Template.chart.onRendered(function(){
this.autorun(function(computation){
...
computation.stop();
});
});
but with this autorun, when chart template is removed from the DOM it is removed automatically.
This is in Meteor documentation here:
The Computation is automatically stopped when the template is destroyed.

Retrieving Data from Meteor Collections

I'm having a few problems when trying to get data from a Meteor Collection and I need some advice.
The collection has been defined, published, and subscribed successfully. If I send the data to a template, it displays fine:
Template.Lists.Projects = function(){
return Projects.find();
};
But I am trying to use the data before displaying it, and this is where I run into problems. First, I'm getting some inconsistencies between find() and findOne(). find(selector) works fine and returns a cursor, but findOne(selector) returns "undefined." I really am only looking for 1 entry, so find() seems unnecessary.
Returns LocalCollection.Cursor:
var find = Projects.find({_id: "3fd33eed-9735-4376-860e-3be383beae2f"});
console.log(find);
Returns undefined:
var find = Projects.findOne({_id: "3fd33eed-9735-4376-860e-3be383beae2f"});
console.log(find);
My next problem arises when using .fetch() on the LocalCollection.Cursor. It returns an empty array.
var find = Projects.find({_id: "3fd33eed-9735-4376-860e-3be383beae2f"});
var fetch = find.fetch();
console.log(fetch);
all this returns is the following line:
[ ]
When I try to specify a specific key from the array I want to display, like:
var find = Projects.find({_id: "3fd33eed-9735-4376-860e-3be383beae2f"});
var fetch = find.fetch();
console.log(fetch.name);
It returns undefined.
I'm still familiarizing myself with Meteor and have never used MongoDB (or minimongo), so I'm probably just making some stupid mistake. If anyone can point it out to me I would be thrilled!
Your results for find() and findOne() are consistent. Basically, Mongo or minimongo is simply not finding a document that matches that _id. FindOne() is precisely like doing a find(selector, options).fetch()[0].
Your Lists.Projects template is likely expecting a collection, array or hash it can iterate over. You cannot return one specific document. If you are using {{#each Projects}} you must provide some way for the template to iterate not just a single value.
I recently had the same problem,
The collection find() returned nothing when used from a query.observe.
The problem was the order of the subscribe of collections.
For example if you have a collection called Lists and one called Projects,
If you're getting the projects by observing a query on lists, and you had :
Meteor.subscribe('Lists');
Meteor.subscribe('Projects');
What happens is the query observe trigger is called, but the Projects are not yet fetched from the server. So Projects.find().fetch().length = 0.
To fix it, simply do
Meteor.subscribe('Projects');
Meteor.subscribe('Lists');
if u've removed autopublish, what if u publish the collection to all user without using a publish name?
Meteor.publish null, ->
Products.find {}
where u subscribe your collection ?
template helper
Handlebars.registerHelp = 'products', (id) ->
Product.find _id: Session.get 'productId'
like if we have price in each product.
the template part looks like...
<template name="products-list">
<div class="products-list">
{{#each products}}
{{> product-item}}
{{/each}}
</div>
</template>
<template name="product-item">
<div class="product-item">
{{price}}
</div>
</template>
the js part, i'll use coffeescript...
Template['product-item'].price = ->
console.log # # # is this as it is product itself, if we have product inserted.
console.log this
return 'the price is: ' + #price
try this way
Meteor.subscribe('testData', function() {
var document = Documents.find();
console.log(document);
});
You are working on the client and you never know when the client got all the data you need. Your functions can be fired when the collections are empty or still not finished synchronized. So you have to make a deferred request to your minimongo (when all data is available local)
And yes you canĀ“t access stuff when its not rendered in the DOM via getElementById() or something but in your case you try to access data from the minimongo (your local mongodb version in the browser) not the DOM so your template is not important here.
Just wait until your subscribtion is ready that your minimongo own all the data with the onReady callback in your subscribtion call and fire your functions.
https://docs.meteor.com/api/pubsub.html#Meteor-subscribe
callbacks (Function or Object).
Optional. May include onStop and onReady
callbacks. If there is an error, it is passed as an argument to
onStop. If a function is passed instead of an object, it is
interpreted as an onReady callback.

Resources