Meteor: data passed to template from Iron Router, is empty at first load - meteor

I'm facing a strange problem.
I'm using Iron Router controller to pass data to template:
Router.route('/wards/add/:_id?', {name: 'wards.add', controller: 'WardAddController'});
WardAddController = RouteController.extend({
action: function() {
this.render('addWard', {
data: function(){
return { hospitals : Hospitals.find({}), hospital_id : this.params._id }
}
});
}
});
I return a variable 'hospitals', that should contain all the collection data.
Template:
<div class="jumbotron">
{{#each hospitals}}
{{name}}<br>
{{/each}}
</div>
At first page load, if I type the directly the url of the page, there are no items.
If I type Hospitals.find({}).fetch() (insecure is active) in the browser console, it return an empty object.
But if i change pages, navigating on the website a while, and return the the listing page, items appears.
Any idea?

In the server folder, add publish.js and inside it add:
Meteor.publish('hospitals', function() {
return Hospitals.find({});
});
Then try subscribing to hospitals from your controller:
WardAddController = RouteController.extend({
action: function() {
this.render('addWard', {
waitOn: function() {
return [
Meteor.subscribe('hospitals')
];
},
data: function(){
return { hospitals : Hospitals.find({}), hospital_id : this.params._id }
}
});
}
});

Related

Adding collection items as routes in Meteor

I have a meteor project where all my users have their own profile page setup in this way using routes:
Routes code:
Router.route('/#:username', {
name: 'profile',
controller: 'ProfileController'
});
ProfileController = RouteController.extend({
template: 'profile',
waitOn: function() {
return Meteor.subscribe('userProfile', this.params.username);
},
data: function() {
var username = Router.current().params.username;
return Meteor.users.findOne({
username: username
});
}
});
Server code:
Meteor.publish('users', function() {
return Meteor.users.find({}, {fields: {username: 1, emails: 1, profile: 1, roles: 1}});
});
Meteor.publish('userProfile', function(username) {
// Try to find the user by username
var user = Meteor.users.findOne({
username: username
});
// If we can't find it, mark the subscription as ready and quit
if (!user) {
this.ready();
return;
}
// If the user we want to display the profile is the currently logged in user
if(this.userId === user._id) {
// Then we return the curresonding full document via a cursor
return Meteor.users.find(this.userId);
} else {
return Meteor.users.find(user._id, {
fields: {
profile: 0
}
});
}
});
I want to do something similar with a pages collection that I've set up. Creating the collection works and the collection page has an _id field that is made upon creation.
Right now the program works nicely for users where mysite.com/# works. Now I want the same thing to work for mysite.com/&
I've basically attempted to do the exact same thing as I did in the above code with the user name but it wasn't working. I've checked to make sure my creation of the collection items are working and they are. But somehow I can't figure out how to do this same thing with collections since I'm relatively new to using routes.
This is what I've attempted:
Here's my routes:
var pageRoute = '/&:_id';
Router.route(pageRoute, {
name: 'page',
controller: 'PageController'
});
PageController = RouteController.extend({
template: 'page',
waitOn: function() {
return Meteor.subscribe('Page', this.params._id);
},
data: function() {
var _id = Router.current().params._id;
return Meteor.pages.findOne({
_id: _id
});
}
});
Server code:
Meteor.publish('pages', function() {
return Pages.find({});
});
Meteor.publish('Page', function(_id) {
// Try find the page by _id
var page = Meteor.pages.findOne({
_id: _id
});
// If we can't find it, mark the subscription as ready and quit
if (!page) {
this.ready();
return;
}
// If the page we want to display is not claimed, display it
if(true) {
return Meteor.pages.find(this._id);
} else {
// Redirect to the page
}
});
The Schema of the Page Collection:
_id: ,
createdAt: ,
CreatedBy: ,
claimedAt: ,
claimedBy: ,
Update:
I've scoped it down to this problem, I get the following error in the console server-side:
I20160202-11:16:24.644(2)? Exception from sub qrPage id 2kY6RKCTuCpBDbuzm TypeError: Cannot call method 'findOne' of undefined
I20160202-11:16:24.645(2)? at [object Object].process.env.MAIL_URL [as _handler] (server/ecclesia.life_server.js:40:33)
I20160202-11:16:24.645(2)? at maybeAuditArgumentChecks (livedata_server.js:1698:12)
I20160202-11:16:24.645(2)? at [object Object]._.extend._runHandler (livedata_server.js:1023:17)
I20160202-11:16:24.645(2)? at [object Object]._.extend._startSubscription (livedata_server.js:842:9)
I20160202-11:16:24.646(2)? at [object Object]._.extend.protocol_handlers.sub (livedata_server.js:614:12)
I20160202-11:16:24.646(2)? at livedata_server.js:548:43
This error occurs whenever I try to direct to mysite.com/&<_id>
Based on this website: https://perishablepress.com/stop-using-unsafe-characters-in-urls/
It looks like # is considered an unsafe character to use in a URL string. On the web page above, it looks like there are several symbols you could use instead as safe characters.
I just tried this on my own machine, and I don't think Meteor plays nicely when the # is introduced in the URL.
This got it working...
Publications:
Meteor.publish('qrpages', function() {
return QRPages.find({});
});
Meteor.publish('qrPage', function(id) {
// Try find the qrpage by _id
var qrpage = QRPages.find({_id: id});
// If we can't find it, mark the subscription as ready and quit
if (!qrpage) {
this.ready();
return;
}
return qrpage;
});
Routes:
var qrpageRoute = '/$:_id';
Router.route(qrpageRoute, {
name: 'qrpage',
controller: 'QRController'
});
QRController = RouteController.extend({
template: 'qrpage',
waitOn: function() {
var id = this.params._id;
return Meteor.subscribe('qrPage', id);
},
data: function() {
var id = this.params._id;
return QRPages.findOne({
_id: id
});
}
});

Meteor: Pulling variable from iron router to a template

I am trying to get two variable from the iron router so that I can essentially call them in my temlate such as {{user.telephone}} and {{pic.profilepic}}.
My code in the router is as follows. (Does Not Work!)
Router.route('/profile/:_id', {
name: 'profile',
template: 'profile',
data:function(){
return {
user:Info.findOne({_id:this.params._id}),
pic: Profilepics.findOne({_id:this.params._id})
};
},
subscriptions: function(){
return {
Meteor.subscribe("userInfo"),
Meteor.subscribe( "profilepic");
},
action:function (){
if (this.ready()){
this.render();
}else {
this.render('loading');
}
}
});
I able to do just one variable with the following code. i.e get {{user.telephone}}. Any chance anyone can help me get both variable instead of just one?
enterRouter.route('/profile/:_id', {
name: 'profile',
template: 'profile',
data:function(){
return {
user:Info.findOne({_id:this.params._id})
};
},
subscriptions: function(){
return Meteor.subscribe("userInfo")
},
action:function (){
if (this.ready()){
this.render();
}else {
this.render('loading');
}
}
});
If you are using the latest version of iron router, i suggest you update the code to something a bit more modern.
First you create a general app controller:
ApplicationController = RouteController.extend({
layoutTemplate: 'DefaultLayout',
loadingTemplate: 'loading_template',
notFoundTemplate: '404_template',
});
Then you start to extend it for different purposes:
ProfileController = ApplicationController.extend({
show_single: function() {
this.render('profile');
}
});
After this you can create your routes for the profile part
Router.route('/profile/:_id', {
controller: 'ProfileController',
action: 'show_single',
waitOn: function() {
return [
Meteor.subscribe("userInfo"),
Meteor.subscribe("profilepic")
];
},
subscriptions: function() {
this.subscribe("somethingyoudontneedtowaitfor", this.params._id);
},
data: function() {
if (this.ready()) {
return {
user: Info.findOne({
_id: this.params._id
}),
pic: Profilepics.findOne({
_id: this.params._id
})
};
}
}
});
It might be a bit more code, but it gives you complete control over what it does. Also, using this, while the router is waiting for the subscriptions to be ready, it displays the loading template defined above. If you don't want to display the loading, you move the subscriptions out of waiton.
Return multiple subscriptions as an array from subscriptions function,
Use return [
Meteor.subscribe("userInfo"),
Meteor.subscribe( "profilepic")
];
instead of
return { Meteor.subscribe("userInfo"), Meteor.subscribe( "profilepic"); which has {} mismatch.
In a similar situation I have
data: function() {
var gridId = this.params._gridId;
return (People.find({gridId: gridId})
&& GridLog.find({gridId: gridId}));
},
with the subscribe calls in the waitOn() handler as part of the Boolean status
waitOn: function() {
return (Meteor.subscribe('people', this.params._gridId)
&& Meteor.subscribe('gridlog', this.params._gridId) );
},
You may have more success just &&ing them together in waitOn() (as strange as that seems).

MeteorJs: Return data Iron:router

Iron router return data is in template but I can't use it.
For example I have db with jobs, where every job has a position (e.g. jobs.position):
ExistJobPostController = RouteController.extend({
layoutTemplate: 'existJob',
data:function() {return Posts.findOne(this.params._id); }
})
Router.map(function() {
this.route('existJob', {
path: '/jobs/:_id',
controller: ExistJobPostController,
});
});
<template name="existJob">
{{position}}
</template>
And nothing happens, I think that it's my fault, but I really can't understand how to fix this.
Can anybody help?
You should first check that the correct data is even being set on your template data context. Here's a quick general summary of how to set the data context and how to access it from various locations:
Router.map(function() {
this.route('index', {
path: '/index',
data: function(){
var obj = {
fname: "Tom",
lname: "Smith"
};
return obj;
}
});
});
Template.index.onRendered(function(){
console.log(this.data.fname);
});
Template.index.events({
'click body': function(e, tmpl){
console.log(tmpl.data.fname);
}
});
Template.index.helpers({
lastName: function(){
return this.lname;
}
});
<template name="index">
You have to use `this` when directly accessing template data from spacebars.
{{this.firstName}}
The following comes from Template.index.helpers:
{{lastName}}
</template>

Page not showing single database entry in Meteor

I have set up my app with Iron Router and have a 'post' page where I would like to show a single post according to its ID. However, this page does not show a post, and instead the post template renders blank (although the url and nav are working fine). Here is my code:
router.js
Router.route('postDetail', {
path: '/posts/:_id',
notFoundTemplate: 'postNotFound',
waitOn: function() {
return [
Meteor.subscribe('post')
]
},
data: function() {
var idVar = this.params._id
return posts.findOne({_id: idVar});
}
});
postDetail.html
<template name="postDetail">
{{address}}
</template>
postsPub.js
Meteor.publish('posts', function () {
return posts.find();
});
Meteor.publish('post', function (id) {
return posts.find(id);
});
It looks like your publisher needs an id. Try this in your waitOn:
Meteor.subscribe('post', this.params._id)
I was able to solve this issue by simply removing the 'post' publish function, and including the following route which refers to the 'posts' publish function instead:
Router.route('postDetail', {
path: '/posts/:_id',
notFoundTemplate: 'postNotFound',
waitOn: function() { return Meteor.subscribe('posts'); },
data: function() { return posts.findOne(this.params._id); }
});

Route loads notFound briefly first

I have a iron route that searches for a collection item based on the url param. If it finds it, it returns the item as a data context, otherwise it renders a notFound template. The code looks like this:
this.route('profileView', {
path: list_path + '/profiles/:_id',
fastRender: true,
waitOn: function() {
if (Meteor.user()) {
return [Meteor.subscribe('singleProfile', this.params._id, Session.get("currentListId"))];
}
},
data: function() {
var profile = Profiles.findOne({
_id: this.params._id
});
if (!profile) {
this.render("notFound");
} else
return profile;
}
});
The problem is the notFound template gets loaded briefly prior to profile getting returned, although I thought the waitOn function would have handled that. What's the correct pattern to have the desired result using iron router? Thanks.
Is it possible that you forgot to configure the loading and dataNotFound hooks?
Router.onBeforeAction('loading');
Router.onBeforeAction('dataNotFound');
If you want to understand what is actually going on here, please look here.
I had to check for this.ready() in data. Updated code
this.route('profileView', {
path: list_path + '/profiles/:_id',
fastRender: true,
waitOn: function() {
if (Meteor.user()) {
return [Meteor.subscribe('singleProfile', this.params._id, Session.get("currentListId"))];
}
},
data: function() {
if(this.ready()){
var profile = Profiles.findOne({
_id: this.params._id
});
if (!profile) {
this.render("notFound");
} else
return profile;
}
}
});

Resources