I am using Meteor 1.0
I have the following code :
/lib/collections.js
Members = new Mongo.Collection('members');
/lib/router.js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() { return Meteor.subscribe('members'); }
});
Router.route('/', {name: 'menu'});
Router.route('/member/new/', {name: 'memberNew'});
Router.route('/member/renew/', {name: 'memberRenewal'});
/server/publications.js
Meteor.publish('members', function() {
console.log("Publishing....");
return Members.find();
});
/client/templates/memberList.js
Template.membersList.helpers({
listMembers: function() {
return members.find().fetch(); >>>>>> Error line
}
});
I get the following error:
Exception in template helper: ReferenceError: members is not defined
at Object.Template.membersList.helpers.listMembers
(http://meteorvb.dhcp.meraka.csir.co.za:3000/client/templates/membersList.js?
I have removed autopublish bit if I change /client/templates/memberList.js to read
Template.membersList.helpers({
listMembers: function() {
return Members.find().fetch();
}
});
Everything works.
Can anyone please help me?
I think it's just a typo where you have used lowercase m instead of upper case M for Members.
Template.membersList.helpers({
listMembers: function() {
return Members.find().fetch(); >>>>>> Error line
}
});
Variables are case sensitive and since the members collection was assigned to "Members" you need to refer it as "Members" elsewhere.
Members = new Mongo.Collection('members');
Related
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"]}
Session.set('coursesReady', false); on startup.
UPDATE:
I made it into a simpler problem. Consider the following code.
Inside router.js
Router.route('/', function () {
Meteor.subscribe("courses", function() {
console.log("data ready")
Session.set("coursesReady", true);
});
}
and inside main template Main.js
Template.Main.rendered = function() {
if (Session.get('coursesReady')) {
console.log("inject success");
Meteor.typeahead.inject();
}
The message "inject success" is not printed after "data ready" is printed. How come reactivity does not work here?
Reactivity "didn't work" because rendered only executes once (it isn't reactive). You'd need to wrap your session checks inside of a template autorun in order for them to get reevaluated:
Template.Main.rendered = function() {
this.autorun(function() {
if (Session.get('coursesReady')) {
console.log("inject success");
Meteor.typeahead.inject();
}
});
};
Probably a better solution is to wait on the subscription if you want to ensure your data is loaded prior to rendering the template.
Router.route('/', {
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',
waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('courses');
},
action: function () {
this.render('Main');
}
});
And now your rendered can just do this:
Template.Main.rendered = function() {
Meteor.typeahead.inject();
};
Don't forget to add a loading template.
To Solve Your Problem
Template.registerHelper("course_data", function() {
console.log("course_data helper is called");
if (Session.get('coursesReady')) {
var courses = Courses.find().fetch();
var result = [ { **Changed**
name: 'course-info1',
valueKey: 'titleLong',
local: function() {
return Courses.find().fetch();
},
template: 'Course'
}];
Session.set('courseResult', result); **New line**
return Session.get('courseResult'); **New line**
,
Explanation
The answer is at the return of the helper function needs to have be associated with reactivity in order for Blaze, template renderer, to know when to rerender.
Non-reactive (Doesn't change in the DOM as values changes)
Template.Main.helpers({
course_data: UI._globalHelpers.course_data ** Not reactive
});
Essentially: UI._globalHelpers.course_data returns an array of objects which is not reactive:
return [
{
name: 'course-info1',
valueKey: 'titleLong',
local: function() {
return Courses.find().fetch();
},
template: 'Course'
},
Reactive
From Meteor Documentation:
http://docs.meteor.com/#/full/template_helpers
Template.myTemplate.helpers({
foo: function () {
return Session.get("foo"); ** Reactive
}
});
Returning Session.get function to Blaze is reactive; thus, the template will change as the values changes.
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'
});
I have a project which includes multiple subscriptions which are shared between routes.
I have been using a session variable to indicate when my subscription is loading. I can use these session variables as helpers in my templates and show different loading whirlygigs on my page. This is great when i have a single subscription (although in this case I could use the "loadingTemplate" i guess).
One subscription per route - no problems:
this.route('foos', {
path: '/foos/;id',
layoutTemplate: 'pagelayout',
yieldTemplates: {
'login_header': {to: 'header'},
'foos': {to: 'main'},
'footer': {to: 'footer'}
},
waitOn: function () {
Session.set('fooLoading',true);
return Meteor.subscribe("foos", this.params.id, Session.get("some-input"), {
onReady: function() {
Session.set('fooLoading',false);
},
onError: function (error) {
Session.set('fooLoading',false);
}
});
}
});
However, with multiple subscriptions I run into trouble because when one subscription is updated the waitOn hook runs, both session variables get set to loading but only one is reset when the subscription is ready.
Two subscriptions per route - problems:
this.route('foobar', {
path: '/foobar/;id',
layoutTemplate: 'pagelayout',
yieldTemplates: {
'login_header': {to: 'header'},
'foobar': {to: 'main'},
'footer': {to: 'footer'}
},
waitOn: function () {
//Can put logic here to figure out which subscription will re-run, but gets hacky and is harder to maintain.
Session.set('fooLoading',true);
Session.set('barLoading',true);
var fooHandle = Meteor.subscribe("foo", this.params.id, Session.get("some-input"), {
onReady: function() {
Session.set('fooLoading',false);
},
onError: function (error) {
Session.set('fooLoading',false);
}
});
var barHandle = Meteor.subscribe("bar", this.params.id, Session.get("some-other-input"), {
onReady: function() {
Session.set('barLoading',false);
},
onError: function (error) {
Session.set('barLoading',false);
}
});
return [fooHandle, barHandle];
}
});
I have been writing logic to decide if each subscription will re-run but this feels hacky and "un-meteor". If there was something on each subscription like onBeforeUpdate that would be awesome. Does such a thing exist or is there a more elegant solution?
You've to use the property this.ready(), check this example from Iron Router documentation: https://github.com/EventedMind/iron-router/blob/devel/examples/waiton/waiton.js
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