Handlebars yield error with '/:username' param - meteor

I set up my router so that if someone types in sitename.com/:username, it goes directly to that users page. But for some reason, when I attempt to do this I sometimes get redirected to my home page and get this error in the console.
Sorry, couldn't find the main yield. Did you define it in one of the rendered templates like this: {{yield}}?
It's even more strange because I would say 60% of the time it loads fine and there are no issues. Does anyone familiar with Meteor, Handlebars, and the Iron-Router package know about this or can help?
If you need more code let me know.
Here is the routing. I added the wait() call at the end to see if that would help. It seems to have helped a bit but the error still occurs.
this.route('userPosts', {
path: '/:username',
layoutTemplate: 'insideLayout',
template: 'userPosts',
yieldTemplates: {
'insideNavigationList': {to: 'list'},
'brandContainer': {to: 'brand'},
'postsPageOptions': {to: 'pageOptions'}
},
waitOn: function () {
return [Meteor.subscribe('userData'), Meteor.subscribe('selectedUser', this.params.username), Meteor.subscribe('posts', this.params.username)];
},
data: function () {
return Meteor.users.findOne({username: this.params.username}) && Session.set('selectedUserId', Meteor.users.findOne({username: this.params.username})._id)
},
before: function () {
if (!Meteor.user()) {
this.redirect('/login')
}
this.subscribe('selectedUser', this.params.username).wait();
this.ready();
}
});

Related

Meteor - data not available with Iron Router

I am trying to achieve something pretty simple: load a template and fill it with data. However, the template is rendered several times, and the first time with null data.
I found various posts about the issue stating that rendering occurs every time the subscription adds a record to the clients database, and tried applying proposed solutions, but I still have a first rendering without data. How can I fix that?
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound'
});
Router.route('/team/:_id/cal', {
name: 'calendar',
waitOn: function() {
return [
Meteor.subscribe('userTeams',Meteor.user()._id),
Meteor.subscribe('teamUsers', this.params._id)];
},
action : function () {
console.log("action:"+this.ready());
if (this.ready() && Template.currentData != null) {
this.render();
}
},
data: function(){
var team = Teams.findOne({_id:this.params._id})
console.log(JSON.stringify(team));
return team;
}
}
);
Console output
Navigated to http://localhost:3000/team/PAemezbavGEmWBNMy/plan
router.js:33 undefined
router.js:33 {"_id":"PAemezbavGEmWBNMy","name":"superteam","createdAt":"2015-10-22T11:51:10.994Z","members":["T6MpawQj75J2HgPi6","4T3StXAaR9iF4sK99","dDe2dJq3wjL2rFB43"]}
router.js:26 action:true
router.js:33 {"_id":"PAemezbavGEmWBNMy","name":"superteam","createdAt":"2015-10-22T11:51:10.994Z","members":["T6MpawQj75J2HgPi6","4T3StXAaR9iF4sK99","dDe2dJq3wjL2rFB43"]}

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 checking document existence before route runs

I have a basic chat room App where you can create a room from the main page which calls this method :
createNewRoom: function (){
//Room containers unique ID for the object
var room = Rooms.insert({
createdAt: new Date()
});
return room;
},
The rooms are routed like this :
Router.route('/rooms/:_id', {
name: 'room',
template: 'chatRoom'
});
I am trying to set it up though so that you can't just type any random ID and get a room, it has to already exist. So I created this iron-router hook:
var checkRoomExists = function() {
var room = Rooms.findOne({_id : this.params._id});
if(typeof(room) == "undefined"){
Router.go('home');
}
else {
this.next();
}
}
Router.onBeforeAction(checkRoomExists, {
only : ['room']
});
room in the checkRoomExists always returns undefined though, even if I test the exact same statement elsewhere with the same _id and the room exists. So if I send the link to someone else it will redirect even if the room exists. Is this the wrong type of hook or is there a better way to accomplish this?'
Edit some additional information:
This is the code that creates a room the first time around :
Template.home.events({
'click #create-room' : function(event){
event.preventDefault();
Meteor.call('createNewRoom', function(error, result){
if (error){
console.log(error);
}else {
Session.set("room_id", result);
Router.go('room', {_id: result});
}
});
}
});
If I try to use the full link after, like http://localhost:3000/rooms/eAAHcfwFutRFWHM56 for example, it doesn't work.
I am trying to avoid users going to some random ID like
localhost:3000/rooms/asdasd if a room with that ID doesn't already
exist.
If you want to do this you can follow the next.
on the Layout configure add this.
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound' //add the notFoundTemplate.
});
and this simple onBeforeAction.
Router.onBeforeAction('dataNotFound', {only: 'room'});
Create some sample template like this.
<template name="notFound">
<span> Dam this route don't exist go back to home
</template>
OPTION
Im not sure if this still working but you can define this on the very last of the routes.js js
this.route('notFound', {
path: '*' //this will work like the notFoundTemplate
});
NOTE
If you don't have layout template use like this.
Router.route('/rooms/:_id', {
name: 'room',
notFoundTemplate: 'authorNotFound',
template: 'chatRoom'
});

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