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

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.

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

Meteor: why autorun executed twice in onCreated

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.

In Meteor, how to short-circuit-assign a helper variable with user object?

I'm running the latest Meteor (v1.1.0.3) on OS X 10.6.8 in Firefox 39.0.
I'm using accounts-ui and accounts-google for login management. I have a hand-rolled profile form with (among other things) a 'name' field. The initial value of this field should be either the name that is already set in their profile or the one that Google supplies.
I've defined the following template helper:
Template.profile_edit.helpers({
my_name: (function () {var u=Meteor.user(); return u.profile.name || u.services.google.name;}())
});
I use the value in my template as {{ my_name }}. When I start meteor everything compiles just fine, but when I load the profile page I get the following Javascript error:
TypeError: u is undefined
...me: (function () {var u=Meteor.user(); return u.profile.name || u.services.googl...
Not immediately relevant, but just for completeness:
After the page loads, the 'name' field in the form is blank.
I am logged in.
When I pull up Meteor's mongo instance in my terminal I can see my user record; the name in the profile is NOT set, the name in the services.google.name IS set.
Why is this error happening and how can I solve it?
The Problem
This is a common issue that people have when first starting out with Meteor. The problem is that when this helper is executed during page load, depending on the response time for the publication from the server, the data for the currently logged in user may not be available yet. This makes it seem intermittent because at times the data is published in time, and others it's not.
Possible Solutions
One possible solution is to install meteorhacks:fast-render. This will publish the logged in user (due to the null publication) with the initial html for the page and guarantee that the user is available when this helper is run. Data other than the currently logged in user will need to be properly set up as subscriptions on the router for fast render to take effect.
The other solution, and one that will work without installation of a new package is to guard against undefined. This will effectively let the helper return undefined when there is no data, but once the data is available the helper will reactively rerun and the proper data will be returned.
Template.profile_edit.helpers({
my_name: function () {
var u=Meteor.user();
if(u){
return u.profile.name || u.services.google.name;
}
}
});
Aside
In your helper I notice that you are using syntax like my_name:(function(){}()). While this will give you what seems like a desired outcome, the problem is that you are immediately invoking this function and assigning its value to the my_name helper instead of assigning it a function that can be called multiple times when the value changes. This breaks reactivity and so the second solution would not work due to it's reliance on it.
Template.profile_edit.helpers({
my_name: (function () {
/*
This is immediately invoked and basically
like using my_name: "bob"
*/
}())
});

In Meteor, is there a way to detect when data is fully loaded to the browser?

This problem probably has been asked before, but I cannot seem to figure out an easy way to do it.
I have a Meteor page that shows messages posted by users in chronological order (newest message at the bottom). I want the page to:
#1) Subscribe to the messages (using publish/subscribe) based on a parameter supplied in the URL
#2) Render the page
#3) Display the messages on the page
#4) Scroll to the bottom.
There's no apparent way to know when 1, 2, and 3 are complete before initiating the scroll to bottom. Meteor does have an observe/added function to do an event when new messages are added to a subscription, however that's only when documents are insertted into Mongo, and it does not trigger when displaying results to the initial subscription.
I'll show code for the steps above:
#1: Subscribe to the messages using publish/subscribe: in /client/messages.js
Template.messages.created = function() {
this.autorun( function() {
this.subscription = Meteor.subscribe("messages", Router.current().params.category);
}.bind(this));
};
#2 Render the page, in /client/messages.html
<template name="messages">
{{#each messages}}
{{messageText}}<br><br>
{{/each}}
</template>
#3: Display the mssages on the page: /client/messages.js
Template.messages.helpers({
messages: function() {
var category = Router.current().params.category;
return Messages.find({category: category}, { sort: { messageDate: 1 } });
},
});
All this works, but does not automatically scroll to the bottom.
I cannot add a jquery scroll command to the Meteor onRendered section because it runs BEFORE the data is written to the DOM. I can do a Meteor.setTimeout to wait 1 second before scrolling to the bottom, but does not work if it takes longer than a second to fill the DOM with subscribed data.
Here's another thing complicating the matter. I am supplying the category variable in the URL. When the client selects another category, using Meteor/Blaze pathFor,
{{pathFor 'messages' channelid='new'}}
the browser does not reload/rerender the page, it simply updates the URL parameter which triggers the autorun to change what messages it has subscribed to. It simply writes the new data to the DOM. Because of this, I cannot to a jquery $(document).ready to detect whether the page is ready (because the page is always ready), and I cannot use some fancy handlebars thing like {{scrollToBottom}} at the end of my {{#each}} in messages.html because that it not re-run.
So, is there a simple way to detect when Meteor/Blaze completely finishes writing new data to the browser?
If I understand correctly, you really just want to know when all of your data is published from the server to the client (so you can then do something, in this case, scroll to the bottom).
All calls to Meteor.subscribe() return a subscription handle. This has a ready() method, that tells you all the data has been sent. This includes those done at a template level (which you might want to consider if appropriate for your use-case). More information here:
http://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe
There are several ways to use this - you need to decide what is appropriate for you. As ready() is reactive, you can just use this. However, you might find it easier to implement an onReady callback within your subscription (see documentation above).
I suspect you will need to implement an autorun within your template rendered. This is to avoid the reverse scenario where the data arrives before the rendering (and so the callback fires too early). Something like this should do the trick (noting you have already set your subscription handle in your template creation function to this.subscription):
Template.messages.onRendered(function() {
var self = this;
this.autorun(function(computation) {
if(self.subscription.ready()){
$(document).scrollTop($(document).height());
computation.stop()
}
});
});
This should ensure both that the function is only called after rendering and the data from the subscription is complete. The autorun is also stopped after executing the scroll to stop continued calling should new documents cause ready() to no longer be truthy (and then become ready again) - which might surprise people with the page re-scrolling to the bottom!

Meteor.user() is undefined when directly access the url

I have a url http://localhost:3000/test :
Template.test.onRendered(function() {
console.log(Meteor.user());
});
If I first open http://localhost:3000/, then click the test link, Meteor.user() will be printed.
But if I directly open http://localhost:3000/test(Input the url in Chrome's address bar and hit enter), Meteor.user() is undefined.
Why??
That's because Meteor logging in process is not instantaneous upon your app first load, it usually takes a few ms before the user actually gets connected, hence Meteor.user() returning undefined in your template onRendered lifecycle event.
I'm unsure what you're trying to achieve but you can solve this problem by introducing reactivity inside your onRendered handler :
Template.test.onRendered(function() {
this.autorun(function(){
console.log(Meteor.user());
});
});
Declaring a reactive computation using Tracker.autorun will allow your code to rerun whenever the value of Meteor.user() is updated, and particularly on initial logging in resume process.

Resources