Do something when the route changes (Iron Router - Meteor) - meteor

I would like to do a "Session.set()" when the route starts and do another one when it stops.
var boxDisplaying = {
onBeforeAction: function() {
console.log("Box is opened");
Session.set('boxIsOpen', true);
this.next();
},
onStop: function(){
Session.set('boxIsOpen', false);
console.log("Box is closed");
}
};
(The other router's declaration part is ok and running well. I think I missed something about "onBeforeAction" and "onStop".)
The problem is that both of them are called when it starts and stops. I want "onBeforeAction" called when we start using the route and "onStop" when we stop. How can I make it work ?
Thanks.

Related

Meteor publication not working

I have Iron Router and quite simple pub/sub.
When publication just returns some specific item - everything works fine. But when it does some logic inside (looping thru another collection) - it doesn't work (Iron Router's loading template keeps showing forever, and looks like no data is coming thru DDP from this publication).
The code of pub:
Meteor.publish('ordersWithState', function(orderState) {
// if uncommented, this line works just fine
//return Orders.find({name:"C02336"});
var temp = Workflows.findOne({name:"CustomerOrder"});
if (temp) {
var stateUuid;
_.each(temp.state, function (state) {
if (state.name == orderState) {
return Orders.find({stateUuid: state.uuid});
}
});
}
});
Router config (if needed):
this.route('ordersList', {
path: '/orders/list/:orderState?',
loadingTemplate: 'loading',
waitOn: function() {
console.log("in ordersList waitOn");
var orderState = this.params.orderState || "Требуется закупка";
return [
Meteor.subscribe('ordersWithState', orderState),
Meteor.subscribe('allSuppliersSub'),
Meteor.subscribe('tempCol'),
Meteor.subscribe('workflows')
];
},
data: function () {
return Orders.find({});
},
onBeforeAction: function (pause) {
this.next();
}
});
The problem is with the logic of your publication here:
if (temp) {
var stateUuid;
_.each(temp.state, function (state) {
if (state.name == orderState) {
return Orders.find({stateUuid: state.uuid});
}
});
}
You are returning something from your inner _.each function, but you are not returning anything from the publication function. So the publication is not returning anything to Iron Router or responding with this.ready();.
It is not exactly clear to me what you want to publish - an array of cursors or perhaps an Orders.find() with an $in: [arrayOfItems]? In any case Iron Router should work fine once the publication is fixed.

iron router syntax: onAfterAction

Router.route('/courses/:_catalog', function () {
var courseCatalog = this.params._catalog.toUpperCase();
Meteor.subscribe("courseCatalog", courseCatalog);
this.render('CourseDetail', {
to: 'content',
data: function () {
return Courses.findOne({catalog: courseCatalog});
}
});
}, {
onAfterAction: function() {
if (!Meteor.isClient) {
return;
}
debugger
var course = this.data(); <======
SEO.set({
title: "course.catalog"
});
}
});
In the above code, please look at the debugger statement. I want to access the data but it seems I am doing something wrong because this.data doesn't exist. I also tried Courses.find().fetch() but I only get an empty array inside onAfterAction. What's the right syntax and what am I missing?
It needs to be inside a this.ready() block:
onAfterAction: function() {
if (this.ready()) {
var course = this.data();
...
}
}
You need to subscribe to data first. Have a look at the waitOn function to do this. The server only sends the documents you subscribed to, and since you didn't subscribe, Courses.find().fetch() returns an empty array.
Also, don't put SEO stuff in onAfterAction. Put it in onRun which is guaranteed to only run once.

Why is data set with Meteor Iron Router not available in the template rendered callback?

This is a bit puzzling to me. I set data in the router (which I'm using very simply intentionally at this stage of my project), as follows :
Router.route('/groups/:_id',function() {
this.render('groupPage', {
data : function() {
return Groups.findOne({_id : this.params._id});
}
}, { sort : {time: -1} } );
});
The data you would expect, is now available in the template helpers, but if I have a look at 'this' in the rendered function its null
Template.groupPage.rendered = function() {
console.log(this);
};
I'd love to understand why (presuming its an expected result), or If its something I'm doing / not doing that causes this?
From my experience, this isn't uncommon. Below is how I handle it in my routes.
From what I understand, the template gets rendered client-side while the client is subscribing, so the null is actually what data is available.
Once the client recieves data from the subscription (server), it is added to the collection which causes the template to re-render.
Below is the pattern I use for routes. Notice the if(!this.ready()) return;
which handles the no data situation.
Router.route('landing', {
path: '/b/:b/:brandId/:template',
onAfterAction: function() {
if (this.title) document.title = this.title;
},
data: function() {
if(!this.ready()) return;
var brand = Brands.findOne(this.params.brandId);
if (!brand) return false;
this.title = brand.title;
return brand;
},
waitOn: function() {
return [
Meteor.subscribe('landingPageByBrandId', this.params.brandId),
Meteor.subscribe('myProfile'), // For verification
];
},
});
Issue
I was experiencing this myself today. I believe that there is a race condition between the Template.rendered callback and the iron router data function. I have since raised a question as an IronRouter issue on github to deal with the core issue.
In the meantime, workarounds:
Option 1: Wrap your code in a window.setTimeout()
Template.groupPage.rendered = function() {
var data_context = this.data;
window.setTimeout(function() {
console.log(data_context);
}, 100);
};
Option 2: Wrap your code in a this.autorun()
Template.groupPage.rendered = function() {
var data_context = this.data;
this.autorun(function() {
console.log(data_context);
});
};
Note: in this option, the function will run every time that the template's data context changes! The autorun will be destroyed along with the template though, unlike Tracker.autorun calls.

Meteor collection lookup on router level

I'm building an app that has versions of pages. I'd like users to be able to visit the page without having to specify the version they wish to start working with. In case no version is specified in the URL, I'd like to look up the latest version of that page and redirect to it.
The problem I'm having is that I can't do any collection lookups on the router level to figure out what version I'm looking for because the collections simply aren't there.
Here's my code so far:
Router.route('page', {
path: '/page/:slug/:version?',
onBeforeAction: function() {
var versionId = this.params.version;
if (!versionId) {
console.log('no version! oh noes!');
var p = Pages.find({slug: this.params.slug});
var newestVersion = Versions.findOne({page: p._id}, {sort: {createdAt: -1}});
versionId = newestVersion._id;
Router.redirect('page', this.params.slug, versionId);
}
this.next();
},
waitOn: function() {
return Meteor.subscribe('page', this.params.slug, this.params.state);
},
data: function() {
return {
page: Pages.findOne({slug: this.params.slug}),
version: Versions.findOne(this.params.version)
}
}
});
Any help or insights are very much appreciated!
Here's the solution I found. The problem is that the onBeforeAction didn't actually wait for the waitOn unless you tell it to.
onBeforeAction: function() {
if (this.ready()) {
if (!this.params.version) {
var p = Pages.findOne({slug: this.params.slug}),
sortedVersions = _.sortBy(Versions.find({page: p._id}).fetch(), function(s) {
return v.createdAt;
});
var newest_version_id = sortedVersions[0]._id;
this.redirect('page', {slug: this.params.slug, state: newest_version_id});
}
this.next();
}
}
I think the key is in the this.ready() check which is only true when all subscriptions in the waitOn are true. I assumed all this happened automagically by simply using the waitOn.

What changes in the execution of an iron router route when coming in on a deep link?

I have a small meteor app going with iron router. Here's my routes.js, which is available to both client and server:
Router.configure({
layoutTemplate: 'defaultLayout',
loadingTemplate: 'loading'
});
// Map individual routes
Router.map(function() {
this.route('comicCreate', {
path: '/comic/create'
});
this.route('comicDetails', {
onBeforeAction: function () {
window.scrollTo(0, 0);
},
path: '/comic/:_id',
layoutTemplate: 'youtubeLayout',
data: function() { return Comics.findOne(this.params._id); }
});
this.route('frontPage', {
path: '/',
layoutTemplate: 'frontPageLayout'
});
this.route('notFound', {
path: '*',
where: 'server',
action: function() {
this.response.statusCode = 404;
this.response.end('Not Found!');
}
});
});
I need to feed some fields from my comic collection document into a package that wraps Youtube's IFrame API, and I'm doing this via the rendered function:
Template.comicDetails.rendered = function() {
var yt = new YTBackground();
if (this.data)
{
yt.startPlayer(document.getElementById('wrap'), {
videoIds: this.data.youtubeIds,
muteButtonClass: 'volume-mute',
volumeDownButtonClass: 'volume-down',
volumeUpButtonClass: 'volume-up'
});
}
};
This works great when I start by going to localhost:3000/ and then clicking on a link set to "{{pathFor 'comicDetails' _id}}". However, if I go directly to localhost:3000/comic/somevalidid, it doesn't, despite the fact that both routes end up pointing me at the same url.
The reason appears to be that when I go directly to the deep link, "this.data" is undefined during the execution of the rendered function. The template itself shows up fine (data correctly replaces the spacebars {{field}} tags in my template), but there's nothing in the "this" context for data when the rendered callback fires.
Is there something I'm doing wrong here?
try declaring the route in Meteor.startup ... not sure if that's the problem but worth a try

Resources