meteorjs - force iron-router to wait the render of all until waitOn finished - meteor

I am looking for a way to wait before rendering anything when a waitOn argument is given.
Currently it works perfectly with the recommended way:
use onBeforeAction ('loading')
use action : function (if (this.ready()) this.render())
waitOn : function () {return [Meteor.subscribe()...]}
So when rendering the routing template the render process waits. But when I include a Template in the main-template the "sub"-Template will be rendered BEFORE the waitOn options ends.
So what is the recommended way to tell the iron-router to wait for the waitOn ready-state before render all included templates and also all yield-sub-templates ?

For me this works great:
this.route('note',{
path: '/note/:_id',
waitOn: function() { return Meteor.subscribe('notes')},
data: function() {
if( this.ready() ){ this.render();
return Notes.findOne(this.params._id);
}else{this.render('loading') }}
});
No content flashing or anything.
But also would like to know of how the guru's deal with it.

Related

IronRouter event function

Does iron router have an event when the Meteor app is loaded?
I would like to run a function when template 'loading' is display, and when app is loaded.
To display a template while loading put this line in the router file.
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading'
});
where loading is the loading template.
Alternatively: pcel:loading package from atmospherejs.
Router Hooks:
You can try to use the router hooks provided by iron-router such as waitOn, onBeforeAction etc. in conjunction with your loading template.
For example:
If you have a route named path and a subscription named foo being accessed on that route:
Router.route('/path', {
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',
waitOn: function () {
// perform action while database subscription loads
console.log('Fetching foo...');
return Meteor.subscribe('foo');
},
onBeforeAction() {
// perform action before route loads
console.log('Loading...');
this.next();
},
action: function () {
// perform action when route loads
// ...
this.render('myTemplate');
},
onAfterAction() {
// perform action after route loads
// ...
}
});
From the official guide, there are a number of options available to perform functions at router level, depending on exactly when you want the action to initiate:
Available Hook Methods
onRun: Called when the route is first run. It is not called again if the route reruns because of a computation invalidation. This
makes it a good candidate for things like analytics where you want
be sure the hook only runs once. Note that this hook won't run
again if the route is reloaded via hot code push. You must call
this.next() to continue calling the next function.
onRerun: Called if the route reruns because its computation is invalidated. Similarly to onBeforeAction, if you want to continue
calling the next function, you must call this.next().
onBeforeAction: Called before the route or "action" function is run. These hooks behave specially. If you want to continue calling
the next function you must call this.next(). If you don't,
downstream onBeforeAction hooks and your action function will not
be called.
onAfterAction: Called after your route/action function has run or had a chance to run. These hooks behave like normal hooks and
you don't need to call this.next() to move from one to the next.
onStop: Called when the route is stopped, typically right before a new route is run.
Plugins:
If you want to reuse the same functionality on multiple routes, you can create route plugins like this:
Iron.Router.plugins.loading = function (router, options) {
// this loading plugin just creates an onBeforeAction hook
router.onBeforeAction('loading', options);
};
Take a look at the full guide to try more hooks and methods that may be useful to you.

MeteorJS with spiderable - or another solution for making App crawlable?

Current we are using a meteor App with the iron:router package and also the spiderable and phantomjs for making this app crawlable by google.
In our special case we have some Routes where we call Meteor methods which are running async before we render the right template into our layout.
When testing spiderable on these routes the template will never get rendered and instead our "loading" template will be the rendered template.
We are testing this with /?_escaped_fragment_=
Now we are looking for a solution to tell spiderable that the page is ready or is not ready so we can control when the page has to be rendered.
Router.route('/awesome-route', function(){
// This is how it could look to tell spiderable that it has to wait
Spiderable.pleaseWait();
// Render the loading template until our route is ready
this.render('loading');
// Waiting for the response of our method
Meteor.call('data', function(err, resp){
this.render('awesome', {
data : resp
});
// This is how it could look to tell spiderable we are ready / always be polite
Spiderable.thanksForWaiting_WeAreReady();
});
}, {
name : 'awesome'
});
When opening now localhost:3000/awesome-route?_escaped_fragment_= we will just see the laoding template ...
The other option for us would be: Is there any alternatives for getting meteor apps crawled by google yet ?
Since spiderable will pre-render your template using phantomjs on server, there is no need for special methods like spiderablePleaseWaitForALittleBitMore_Please
You can just say to your iron:router that template is not rendered yet. Use onBeforeAction hook:
Router.route('/awesome-route', {
name : 'awesome',
template: "awesome",
loadingTemplate: "loading",
onBeforeAction: function(){
var next = this.next;
Meteor.call('data', function(err, resp){
next();
});
}
});

Is there a way to prevent the loadingTemplate from showing up on "fast" routes?

Most of the time, my search returns so fast that it's not worth flashing the loading template to the user...(in fact, it's distracting, as people are fine with a blank screen if the results are coming a split second later)...Is there any way to prevent the loading template from showing if the waitOn is only waiting for a short amount of time?
Here's my configuration
Router.route('/search', {
waitOn: function () {
return searchSubsManager.subscribe("search", Session.get('searchString'));
},
action: function () {
this.render('searchResults');
}
});
I saw that with this package:
https://github.com/Multiply/iron-router-progress
you can control whether it shows up for fast routes, but I don't need all that functionality, nor do I want the progress bar it provides... I'm just wondering if the basic iron router/ waitOn functionality can provide this ability.
There is not configuration to use on the waitOn function, but Why don't you create another layout template, and use it to show that fast routes?
<template name="noLoading">
{{> yield}}
</template>
Router.map(function () {
this.route('fastRoutes', {
path: '/someRoutes',
template: 'myHomeTemplate',
layoutTemplate: 'noLoading',
});
});
Update
or use the sacha:spin package and change the class name depending on the duration of the query.
if(queryDuration){
Meteor.Spinner.options = {
className: 'none'
}
}else{
Meteor.Spinner.options = {
className: 'spinner'
}
}

How to pause Iron Router template rendering until all data is available?

Current common Iron Router pattern is to display a loading template while waiting for data to be available. But I would prefer simply to wait on the previous rendered template/data context until data is available and then trigger rerendering. Data is quickly available, so that short flicker of loading template is worse than a short delay user will experience after a link click.
Does this pattern work for you?
Router.route('/', {
name: 'nameOfTemplate',
data: function() { return CollectionName.find({title: 'nameOfMongoDBQuery'}); },
waitOn: function() { return Meteor.subscribe('nameOfSubscription'); } // waits until resources arrive before rendering page
});
You'll want to specify an explicit action function like so:
action: function() {
if (this.ready()) {
this.render();
}
}
This will just not render anything until the data is around.

Iron Router onBeforeAction isn't being called

I have a /user route set up, which is supposed to render the login template if the current user isn't logged in. The entire router has a waitOn that waits for the currentUser subscription to finish. The problem is that when I go to /user it simply renders the dataNotFound template instead.
Here's the snippets of code that are relevant to this situation. I've been careful to show you them in the order they're defined in my lib/router.js file.
Router.plugin('dataNotFound', {notFoundTemplate: 'notFound'});
Router.onBeforeAction(function () {
console.log(Meteor.userId())
if (!Meteor.userId()) this.render('login');
else this.next();
}, { only: ['user'] });
Router.configure({
waitOn: function () { return Meteor.subscribe('currentUser'); }
});
Router.route('/user', {
name: 'user',
template: 'userView',
data: function () { return Meteor.user(); }
});
That console.log above doesn't even ever fire. It seems to me that since it should be a reactive function that even if initially the dataNotFound is rendered, then soon after that the onBeforeAction should be fired and render the login template, right?
It's very bizarre that your console log doesn't even fire. I have a few ideas, but first want to address the last piece of your question.
The dataNotFound plugin is triggered when the data function fires on your rout. This means it is bypassing your onBeforeAction hook altogether, and not that it isn't getting there.
One thing I can think of that might be worth trying would be wrapping the 'user' route action in a if ( this.ready() ) statement:
edit:
Router.route('user', {
// Your other stuff
action: function() {
if this.ready() {
this.render();
},
The reason I suggest this is just that you are using a waitOn statement, but I'm not 100% sure how that works if you don't have a this.ready() somewhere in your route, because that's what (again, from my reading of the documentation, have not fiddled around with it) tells the router what to wait before executing. Possibly it's not waiting at all right now.
I had a problem with onBeforeAction hook after upgrading from meteor 0.9.1 to 1.
It didnt get fired when it should. Like after I log out, I enter address manually and instead of login page I can see the actual site waiting for data that never comes. onRun hook solved it, but as docs says it gets fired only once.
Router.onRun(function () {
if (!Meteor.userId()) {
this.render('login');
} else {
this.next();
}
}, { only: ['user'] });
Try swapping out Meteor.userId() with Meteor.user() in your if statement. See this link for reference on how to handle checking for the user in a before filter: http://www.manuel-schoebel.com/blog/meteorjs-iron-router-filters-before-and-after-hooks.
I found the issue here.
According to this post, thanks to Steeve Cannon. The problem is on waitOn function.
Probably when you logginOut the subscribe to currentUser will fail, and this will make your app in infinite waiting. onBeforeAction runs after waitOn
You will need to check all variables in you Publish to insure waitOn complete successfully.
Put an if statement inside the waitOn function.
waitOn: function () {
if(Meteor.userId())
return Meteor.subscribe('currentUser');
}
Refer this comment to know why this is happening: https://github.com/iron-meteor/iron-router/issues/1010#issuecomment-72210587

Resources