My app runs fine locally, but when I deploy it on xxxx.meteor.com, I'm getting the Iron Router "Organize your Meteor application" splash page. Additionally, once the browser hits the first Basecontroller.extend({, I get a ref. error that "BaseController is not defined". Maybe something to do with the load order of the route controller specific to deployment? This is my code:
client/main/routes.js
Router.route('/', {
name: 'splash',
controller: 'SplashController'
});
Router.route('/login', {
name: 'login',
controller: 'LoginController'
});
Router.route('/home', {
name: 'home',
controller: 'HomeController'
//...etc
client/main/controller.js
BaseController = RouteController.extend({
layoutTemplate: 'layout',
requiresAuth: false,
publicOnly: false,
// Called before anything happens
onBeforeAction: function () {
if (!Meteor.userId() && this.requiresAuth) {
return this.redirect('/login');
}
if (Meteor.userId() && this.publicOnly) {
return this.redirect('/home');
}
}
});
client/(all templates)/controller.js
TemplateController = BaseController.extend({
requiresAuth: false,
publicOnly: true,
action: function () {
this.render('template', {
to: 'content'
});
}
});
The issue belong in loading only. So, code which should always load first, try put inside <app-root>\lib directory.
Like your case, put route.js and controller.js inside lib directory.
Read more about loading order in meteor - http://docs.meteor.com/#/full/structuringyourapp
Related
I would like to the difference if any in the following ways of subscribing data,
using waitOn
waitOn:function(){
Meteor.subscribe('//some published function)
}
using onBeforeAction
Router.onBeforeAction : function(){
Meteor.subscribe('//some published function)
}
using subscriptions
subscriptions: function() {
this.subscribe('items');
}
If you want to publish data only for authorized users, it is possible that you check (if user is authenticated) for a route, in onBeforeAction. Something like:
Router.map(function(){
this.route('home', {
path : '/',
template : 'home'
});
this.route('register', {
path : '/register',
template : 'register'
});
this.route('login', {
path : '/login',
template : 'login'
});
this.route('requestlisting', {
path : '/requestlisting',
template : 'requestlisting',
waitOn : function(){
return Meteor.subscribe('RequestsPublication');
}
});
...
});
var requireLogin = function () {
if(!Meteor.user()){
if(Meteor.loggingIn()){
this.render(this.loadingTemplate);
}else{
Router.go('login');
}
} else {
this.next();
}
}
Router.onBeforeAction(requireLogin, {only: ['requestlisting',...]});
In this example, in onBeforeAction, routing occurs for 'requestlisting' only if a user logged in, after then it makes sense to subscribe to any data.
I am looking to restrict access to a page using iron router, but the before function doesn't seem to be running.
# Limit which challenges can seen to members.
isMemberHook = () ->
challenge = Challenges.findOne _id: #params._id
if Meteor.userId() in challenge.members
#next()
else
#redirect '/'
Router.onBeforeAction isMemberHook,
only: ['/challenge/:_id']
Turns out that for routes with "/" you need to use ".".
So in this case I used:
only: ["challenge.:_id"]
Problem solved!
You have to be careful to account for when a user is logged in, not logged in, and when they are logging in.
The following is code for a requireLogin function that I place in a onBeforeAction hook.
If there's no Meteor user, check to see if they're logging in, and if so, render a loading template.
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('AdminLogin');
}
} else {
this.next();
}
};
Router.onBeforeAction(requireLogin, {only: ['AdminMain', 'postSubmit', 'appointmentsList', 'AppointmentShow']});
EDIT
Ok, this is what's happening:
https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#applying-hooks-to-specific-routes
What's not perfectly clear from the documentation is what kind of arguments the array takes in the onBeforeAction
Router.onBeforeAction(requireLogin, {only: ['routeName', 'anotherRouteName']});
Here's a set of routes that work with the hook:
this.route('/order', {
template: 'order',
name: 'order',
layoutTemplate: 'layout'
});
this.route('/order/:_id', {
template: 'order',
name: 'orderEdit',
layoutTemplate: 'layout',
data: function(){
var id = this.params._id;
alert(id);
}
});
var beforeHook = function(){
console.log("beforeHook run!");
alert("safsf");
};
Router.onBeforeAction(beforeHook, {only: ['orderEdit']});
This works fine:
Router.route('/', function () {
this.render('home');
});
Router.route('/about', function () {
this.render('aboutView');
});
However, this gives an error (RouteController 'HomeController' not defined):
var HomeController, MainController;
HomeController = RouteController.extend({
action: function() {
this.render('home', {
});
}
});
MainController = RouteController.extend({
action: function () {
this.render('aboutView', {
});
}
});
Router.route('/', {name: 'home', controller: 'HomeController'});
Router.route('/about', {name: 'about', controller: 'MainController'});
I've tried various permutations (taken from the IronRouter documentation) but there is still the same error.
What am I doing wrong?
Either remove this line :
var HomeController, MainController;
To make these global variables.
Or remove the quotes when specifying controllers :
Router.route('/', {name: 'home', controller: HomeController});
Router.route('/about', {name: 'about', controller: MainController});
Why is that so ?
When you define controllers with iron:router you can use 2 different syntax, using a variable identifier of a string.
If you use a variable identifier, the controller has to be declared as a local (file-scoped) variable using the var keyword.
However if you use a string, the controller will get looked as a global variable (a property of the window object), and global variables in Meteor are defined without the var keyword.
Usually you will define your controllers in different files (that's the point of controllers, externalizing routing logic) so it is more common to use the string syntax.
Is there a way to gain access to the Template instance from a global helper and vice versa?
/lib/route.js (with Iron Router):
Router.route('/', {name: 'home.view', controller: 'homeController'});
homeController = RouteController.extend({
template: 'home',
waitOn: function () {
Meteor.subscribe("Person", Meteor.userId());
},
data: function () {
// return some data;
}
});
homeController.helpers({
templateInstanceHelper: function () {
// Access a "global" helper here
}
});
/client/helpers.js:
Template.helpers("globalHelper", function () {
// Access the template instance helper here
});
Have you considered defining a global method instead? Instead of registering with Meteor Templates, just define it as
globalHelperFunc = function(templateVar) {
// do work
}
Note that this need to be in "lib" folder, so maybe (/lib/helpers.js)
Reference: Global function for Meteor template helper
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