Iron router slow to change routes - meteor

When clicking a link, or loading via console with Router.go(...) there is a noticeable pause between calling the route and my app doing anything.
There are no DDP calls being made during this pause and I added debugging to all my templates for rendering and no re-rendering is taking place. I can't for the life of me figure out what is causing this pause, you can see this in action at http://riustats.com

Bassed on #below9k comment, seems like you are trying to load big amount of data in the desired route.
Be sure don't have some observersChanges also.
A good solution could be using the fast-render package.
Code Example.
Router.route('leaderboard', {
waitOn: function(){
return Meteor.subscribe('leaderboard'); //you big collection.
},
fastRender: true // important to render the route fast.
});

Related

How to make react application SEO friendly

If I want to use Google analytics and at the same time make my site SEO friendly in React, what is the best way?
At the moment I use react-router and a flux pattern where I change the route and then in componentDidMount I fire an action that loads my data through ajax and then updates the store which emits the change and finally I re-render the components that are affected. During the ajax loading I dispatch an event so that my store knows ajax is loading and render a spinner in my component.
I have noticed that when I send my tracking data to Google the ajax has not finished loading and I only send the new page URL not the title or any other data which I load through ajax (I guess this is bad from an SEO perspective and it's definitely bad for my GA data).
This is my GA setup (I use react-ga):
Router.run(routes, Router.HistoryLocation, function(Handler, state) {
ga.pageview(state.pathname);
React.render(<Handler />, document.body);
});
Typical component setup (which allows me to render the correct data based on the URL):
componentDidMount: function() {
ItemStore.addChangeListener(this._onChange);
if(itemSlug) {
ItemActions.loadItemBySlug(this.props.slug);
}
}
I want to be able to have a single point of GA tracking if that is possible. I also want the SEO handling to be correct for the page:
Page title
OG data
H1
etc ...
What is the best approach to solve this?
Should I use the willTransitionTo option in react-router for this? (is it possible to use a loading spinner if I opt for this solution?)
statics: {
willTransitionTo: function (transition, params, query, callback) {
// LOAD AJAX HERE ?
callback();
}
}
How would I go about the willTransistionTo solution in a proper way, can't seem to find any good examples that relate?
Should I add more code, or is it clear what I'm trying to achieve?
Trigger the ga.pageview after the store gets the new data and renders.
The appropriate way to handle SEO in react is to render at the server side isomorphically with React.renderToString, so that any search engine will see the page in its complete state, rather than just Google's spider and only on those occasions when you manually call it.
This was one of the original design criteria of React.

Reinitialize library when new data is available

I'm currently developing a Meteor application where I use the video.js-Library.
I have the following template:
template(name='foo')
.video.embed-responsive.embed-responsive-16by9
with richMediaContent
video#video.video-js.vjs-default-skin.vjs-big-play-centered(controls='' preload='auto')
source(src='{{video.videoUrl}}' type='video/mp4')
p.vjs-no-js {{i18n 'videoTagNotSupported'}}
Initializing the video.js-Library after the template is rendered works fine.
Template.foo.onRendered ->
videojs document.getElementsByClassName('video-js')[0], {}
But the videojs-Library is not reinitialized if the same template is rendered with a different video (with a different richMediaContent).
I've already tried to move the video-Part in an own template and included it in the foo-Template so that the onRendered-Call should get called every time a new video is loaded. But this doesn't seem to work.
Do you have any idea how I can reinitialize the library if the video changes?
Thanks in advance!
New answer
Indeed, when your route changes but uses the same template, the said template does not get rendered again, therefore your js plugin call will not trigger a second time. What you can do instead is call your js plugin in an onAfterAction call, within your route definition:
Router.route('/video/:_id', {
name: 'video_page',
template: 'foo',
// ...
onAfterAction: function () {
videojs document.getElementsByClassName('video-js')[0], {}
}
});
Previous answer
I think you are looking for the almighty this.autorun. At the end of your onRendered function, it should look like this (I type it in pure javascript)
this.autorun(function () {
var video = Session.get("video"); // reactive data
videojs document.getElementsByClassName('video-js')[0], {}
});
The idea is that the first line must include, within the autorun function, a way to get your reactive data. In that case, I use the Session which is reactive. Collections are also reactive, so another way would be something like Videos.findOne();. This will depend on how you get that video element.
What this does is that any time the reactive data changes, the callback for this.autorun will run again, and your video plugin will be reset.

Spacebars-generated dynamic links do not trigger Iron Router in Meteor?

I've got this in my routes:
Router.route('/videos/:id', {
name: 'VideoPage',
data: function(){
return Videos.findOne(this.params.id);
}
})
This template shows up at the route above:
Template.VideoPage.helpers({
'videoIds': function(){
var myVideoIds = [
"23456",
"h5e45t",
"f4e4w",
"g6h4h"
];
return myVideoIds;
}
});
HTML:
<template name="VideoPage">
{{videoTitle}}
<p>Click the links below to get to a new video page</p>
{{#each videoIds}}
<a href="/videos/" + {{this}}>
{{/each}}
</template>
When I click on a link, the URL in the browser changes from something like /videos/23456 to something like /videos/f4e4w, but the request never actually goes through Iron Router and the Router.route function. I put a debugger before Router.route and it triggers on initial page load but does NOT trigger when the links are clicked.
I understand that Iron Router's default behavior is NOT to re-render a template the user is currently on, but this is the same template with different url params that are used to change the data in the template, so IMO it should still re-render.
Ok, false alarm. It appears that Router.route DOES fire and updates the data context every time the params._id changes. I was placing the debugger outside of the Route function when I should have been placing it inside of the data context function.
The example that I gave in this answer was also a highy, highly simplified example. It wasn't working in my real-life project due to something else (a complex video iframe generator) that was being refreshed improperly.
But rest assured that going from /videos/f4e4w to /videos/23456 by clicking a link DOES still go through Iron Router and the params does get read and the data does get updated.

jQuery widget (jPanelMenu) stops working on meteor route change

I have implemented jPanel Menu via a rendered template, which works great, until a route has been changed, then the menu stops working. Here is the code I am using to evoke the plugin.
Template.mobileMenu.rendered = function(){
var jPM = $.jPanelMenu({
menu: '#mobile-menu',
trigger: '.menu-trigger'
});
jPM.on();
};
The template is loaded on all pages in the footer. I am thinking it needs to be rerun on route change, OR prevented from being rerun. I am not sure which. Thanks for any tips.
This sounds similar to the problem many people have with using 3rd-party ui components. I put together a working example of using a modal dialog component which may be of some help:
https://github.com/alanning/meteor-modal-example
Also I should point out that the new Meteor UI rendering system, "Blaze", should eliminate these kinds of issues. I would expect Blaze to be released soon.
(For those visitors coming from the future, at the time of writing Meteor v0.7.0.1 is the latest version.)
The solution was to wrap it in an if not rendered, to prevent it from being re-rendered on template / route changes.
Template.mobileMenu.rendered = function(){
if (!this.rendered){
var jPM = $.jPanelMenu({
menu: '#mobile-menu',
trigger: '.menu-trigger'
});
jPM.on();
this.rendered = true;
}
};

Iron Router shows my Not Found template before loading with WaitOn: Meteor

I put my subscribe inside a WaitOn function in a controller with Iron Router / Meteor, and while the subscribe is populating the browser shows my notFoundTemplate: 'notFound'.
How to get around this? I thought WaitOn would actually, you know... wait?
My path is dependant on the subscribe containing the URL. i.e.
www.myapp.com/Fred
this.route('userShow', {
path: '/:owner_name',
controller: 'UserShowController'
});
waitOn should actually wait, if the controller is written correctly.
Things to check:
Are you publishing the collection on the server?
Did you pass the data correctly to the template? Your data function in the UserShowController should look something like data: function () {return People.findOne({name: this.params.owner_name});},
Are you navigating your browser to a valid :owner_name?
Hope this helps. Good luck!

Resources