Meteor Tracker error after multiple routes - meteor

When a running a particular route, I'm getting an error (see below), but ONLY if I've run one or more other routes before running this one. It doesn't matter what those other routes are. So if I reload the home page, and then click to this route, no error. But if I reload home, go to any other route, and then the one in question, I get this error:
Exception from Tracker recompute function: undefined debug.js:43
Error: {{#each}} currently only accepts arrays, cursors or falsey
values.
at badSequenceError (http://localhost:3000/packages/observe-sequence.js?hash=550c39b36ab0e65194ea03cdc7ecbe99dcdd07f6:174:10)
at http://localhost:3000/packages/observe-sequence.js?hash=550c39b36ab0e65194ea03cdc7ecbe99dcdd07f6:139:17
at Object.Tracker.nonreactive (http://localhost:3000/packages/tracker.js?hash=f525263111eb9d90f4ce1ad064f97aca4a6c1b07:631:12)
at http://localhost:3000/packages/observe-sequence.js?hash=550c39b36ab0e65194ea03cdc7ecbe99dcdd07f6:116:15
at Tracker.Computation._compute (http://localhost:3000/packages/tracker.js?hash=f525263111eb9d90f4ce1ad064f97aca4a6c1b07:339:36)
at new Tracker.Computation (http://localhost:3000/packages/tracker.js?hash=f525263111eb9d90f4ce1ad064f97aca4a6c1b07:229:10)
at Object.Tracker.autorun (http://localhost:3000/packages/tracker.js?hash=f525263111eb9d90f4ce1ad064f97aca4a6c1b07:604:11)
at Object.observe (http://localhost:3000/packages/observe-sequence.js?hash=550c39b36ab0e65194ea03cdc7ecbe99dcdd07f6:113:31)
at . (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:2763:43)
at fireCallbacks (http://localhost:3000/packages/blaze.js?hash=ef41aed769a8945fc99ac4954e8c9ec157a88cea:1955:26)
undefined
My Router code:
Router.route('/exercises', {
name: 'exercises',
waitOn: function() {
this.exercisesSub = Meteor.subscribe('exercises');
this.progressSub = undefined;
if (Meteor.userId() && Meteor.user() && Meteor.user().emails[0].verified) {
this.progressSub = Meteor.subscribe("progress", Meteor.userId());
}
return [this.exercisesSub, this.progressSub];
},
data: function() {
if (this.exercisesSub.ready()) {
if (this.progressSub === undefined || this.progressSub.ready()) {
var data = {
subHeader: 'Exercises',
exercises: Exercises.find({}, {sort: {order: 1}}),
};
if (this.progressSub !== undefined) {
data.progress = Progress.findOne({});
}
return data;
}
}
}
});
My HTML:
<template name="exercises">
{{#each exercises}}
<exercise id="{{_id}}">
<h2>{{name}}</h2>
<completedBox></completedBox><completedBox></completedBox><completedBox></completedBox>
<description>{{description}}</description>
<criteria>Criteria: <b>{{criteria}}</b></criteria>
Highest score: <score>0%</score>
{{>button name="launch" text="LAUNCH"}}
</exercise>
{{/each}}
</template>
The returned collection Exercises contains the same data with or without error, but for some reason Meteor doesn't see that data if I've loaded previous routes. Any ideas?

Related

Getting Data from Server side HTTP.call to Client template

I currently use meteor for a microproject of mine to get a bit usage experience with it. Shortly after setting up I ran into some trouble getting Data i recieve from an API call to a third party site to the client into the template. I checked the usual places for answers and found some information but nothing seems to get it working for me.
So I have a simple API Call to the Steam Web Api:
Meteor.methods({
getPlayerStats: function() {
return HTTP.get("http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=730&key=XXXXXXXXXXXXXXX&steamid=XXXXXXXX");
}
});
(Api key and steam id removed for anonymity purpose, but the call indeed returns a valid response)
So I use Iron Router for template rendering.
Router.route('/profile/:steamid', {
name:'profile',
template: 'profile',
data: function() {
Meteor.call('getPlayerStats', function(err, res) {
if(err) {
return {err: err, stat: null};
} else {
var redata = JSON.parse(res.content);
var stats = redata.playerstats.stats;
console.log({err: null, stats: stats});
return {err: null, stats: stats};
}
});
}
});
So as you can see i return an object in the data method containing either err or a parsed version of the result i get from the api call. The console.log actually returns everything as intended in the client browser, that is an object like this:
{err: null, stats: [{name: "xx", value: "XY"},...]}
And now the part that actually gets me wondering, the template:
<template name="profile">
<p>{{err}}</p>
<ul>
{{#each stats}}
<li>{{name}} - {{value}}</li>
{{/each}}
</ul>
</template>
Which fails to render anything, not the err (which is null and therefor not very important) but neither the stats array.
Anyone has any idea where I went wrong on this one?
You cannot return data from asynchronous call. Instead, You can do it in the template's created function by using ReactiveVar or Session Variable like this
Template.profile.created = function () {
// or Template.profile.onCreated(function () {
var template = this;
template.externalData = new ReactiveVar(null);
Meteor.call('getPlayerStats', function(err, res) {
if(err) {
template.externalData.set({err: err, stat: null});
} else {
var redata = JSON.parse(res.content);
var stats = redata.playerstats.stats;
console.log({err: null, stats: stats});
template.externalData.set({err: null, stat: stats});
}
});
};
// }); //comment the above line and use this, if you used onCreated instead of created.
Then in your helpers,
Template.profile.helpers({
externalData: function () {
var template = Template.instance();
return template.externalData.get();
}
});
Then in your template html,
<template name="profile">
{{#if externalData}}
{{#if externalData.err}}
<p>There is an error. {{externalData.err}}</p>
{{else}}
<ul>
{{#each externalData.stats}}
<li>{{name}} - {{value}}</li>
{{/each}}
</ul>
{{/if}}
{{/if}}
</template>

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'
});

Unable to pass result to router after calling Meteor.methods

I encounter an error using Meteor. I call an Method.method.
Template.WelcomeTemplate.events({
'click #btn-findgame': function(e) {
e.preventDefault();
console.log('clicked find game button');
Meteor.call('allocateGame', function(error, id) {
if (error) {
alert(error.reason);
} if (id) {
Router.go('gameRoom', {_id: id})
}
})
}
})
With my Method, I check if there is an room available, create one when the isn't otherwise join. And return the ID of this room.
Meteor.methods({
allocateGame: function () {
console.log('allocateGame method called')
var user = Meteor.user();
// find game where one player is in the room
var gameWaiting = Games.findOne({players: {$size: 1}})
if (!gameWaiting) {
console.log('no game available, create a new one');
var newGameId = Games.insert({players: [user._id], active: false, finished: false});
GameDetails.insert({gameId: newGameId, gameData: []});
return newGameId
} else {
if (_.contains(gameWaiting.players, user._id)) {
console.log('Cannot play against yourself sir')
} else {
console.log('Joining game');
Games.update({_id: gameWaiting._id}, {
$set: {active: true},
$push: {players: user._id}
});
return gameWaiting._id;
}
};
}
})
And my Router:
Router.map(function () {
this.route('welcome', {
path: '/',
controller: WelcomeController})
this.route('gameRoom', {
path: '/game/_:id'
})
});
The Error I recieve is:
Exception in delivering result of invoking 'allocateGame': TypeError: Cannot read property 'charAt' of null
at Object.IronLocation.set (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:1293:12)
And indeed, If I don't return an ID the Routing will continue as normal. However when I return an ID in my WelcomeTemplate an error will occur.
EDIT:
Even thought my MongoDB is updating my MiniMongo DB is empty. There must be a problem with syncing. Any idea where to look?
In the route, you set the path to be '/game/_:id', that is, a parameter with the name id. In your call to Router.go, you pass a parameter with the name _id.
Don't know if this solves your problem, but it's an error.
This kind of embarrassing taking in account how many hours I've spent on fixing this. The error was created because of an error in my routers.js
this.route('gameRoom', {
path: '/game/_:id'
})
Should be:
this.route('gameRoom', {
path: '/game/:_id'
})
Happy coding.

Default error page

I would like to define a default error page in meteor. That is if application is crashing or other error occurs the user should be redirected to a "friendly" page that says something like : system is unavailable , please contact etc etc.
Is there any way to accomplish this or something similar ?
Thank you
You have to use BackboneJS(Backbone Router) for routing. With this code the session variable 'page_type' let's you know if you are on a wrong url.
var BackboneRouter = Backbone.Router.extend({
routes: {
"/": "default",
":error": "list"
},
default: function () {
Session.set("page_type", "default");
},
error: function () {
Session.set("page_type", "error");
}
});
Router = new BackboneRouter;
Meteor.startup(function () {
Backbone.history.start({pushState: true});
});
Now you can use the 'page_type' to tell the template engine which template to load.
Template.tmp.route = function () {
if (Session.get("page_type") == "default") {
return true;
} else {
return false;
}
<template name="tmp">
{{#if route}}
{{> default}}
{{else}}
{{> error}}
{{/if}}
</template>

Resources