I want to implement an inheritance scheme for my routes. I want all my routes to inherit from my ApplicationController
ApplicationController = RouteController.extend({
subscriptions: function() {
this.user = Meteor.subscribe('userEarnings', Meteor.userId());
},
data: function() {
return {
currentUser: Users.findOne(),
userReady: this.user.ready()
};
}
});
Which makes sure all custom fields are subscribed to on my user collection. I have the above stored in a file client/lib/routes/main.js. I want to be able to extend my other controllers in client/lib/routes/*.js with ApplicationController, like this
historyController = ApplicationController.extend({
layoutTemplate: 'dashboardLayout',
subscriptions: function() {
this.subs = Meteor.subscribe("userPurchaseHistory", Meteor.userId());
},
data: function() {
console.log(this.user.ready());
return {
purchases: Purchases.find(),
ready: this.subs.ready()
};
}
});
Router.route('history', {
path: '/history',
loginRequired: 'entrySignIn',
controller: historyController
});
but currently, I get
Uncaught ReferenceError: ApplicationController is not defined
How can I access ApplicationController outside of its file? I thought that by not using var, the variables would be globally accessable.
This is a load order issue. As per the docs:
File Load Order
All files that match main.* are moved after everything else, preserving their order.
...
Within a directory, files are loaded in alphabetical order by filename.
Try putting the ApplicationController definition in client/lib/routes/application-controller/application-controller.js or something like that to make absolutely sure it runs before any of the other controllers try to inherit from it. Provided it's not called main.js, having it in a subdirectory will ensure it's added before the stuff in the parent directory and obviate the potential alphabetical order problem.
Related
I'm using FlowRouter for client side routing and I want to load different views and angular module according to the entered rout. For example:
FlowRouter.route('/u/:id', {
name: 'Messages.show',
action(params, queryParams) {
check(params.id, String);
hash = params.id;
require('./u.html');
}
});
FlowRouter.route('/m/:id', {
name: 'Messages.show',
action(params, queryParams) {
check(params.id, String);
hash = params.id;
require('./m.html');
}
});
I want to load completely different templates and files in for each route
I have 2 controllers (iron-router), one for access bits (login etc.) and one for the logged in area. But for some reason one of my routes is choosing to use the wrong controller, even though I'm explicitly stating which one to use. Here is the code:
// Controllers
AccessController = RouteController.extend({
layoutTemplate: 'AccessMaster',
onBeforeAction: function () {
if (Meteor.user()) { // If user is logged in then take them to the Dashboard
this.redirect('/app/dashboard');
} else {
this.next();
}
}
});
DashboardController = RouteController.extend({
layoutTemplate: 'DashboardMaster',
onBeforeAction: function () {
if (!Meteor.user()) { // If user is not logged in then take them to the login
this.redirect('/app/login');
} else {
this.next();
}
}
});
// Routes
Router.route("/app/signup", {
name: 'Signup',
controller: 'AccessController'
});
Router.route("/app/login", {
name: 'Login',
controller: 'AccessController'
});
Router.route("/app/account", {
name: 'Account',
controller: 'DashboardController',
loadingTemplate: 'Loading',
action: function () {
this.render('Account');
}
});
Router.route("/app/dashboard", {
name: 'Dashboard',
controller: 'DashboardController',
loadingTemplate: 'Loading',
waitOn: function () {
…
},
action: function () {
this.render('Dashboard', {
data: {
…
}
});
}
});
When I visit app/account I'm redirected to app/dashboard, as directed in the AccessController. Why is the app/account route using the wrong controller logic?
Edit: Oddly, if I remove the controller declaration in the offending route (controller: 'DashboardController') then the template loads fine. So it only uses the wrong controller when I ask it to us a controller.
I must be missing something but that's awfully odd.
I think that your problem comes from the fact that you are using Meteor.user() in both controllers, which is the actual user document. And like any other collection it may not be immediately ready when the application starts.
If you add a console.log(Meteor.user()) in your controllers, you will see that it is first briefly undefined before returning the user document.
So the route is using the right controller but Meteor.user() is undefined so you are redirected to /app/login where Meteor.user() (probably ready now) returns the documents so you get redirected to /app/dashboard.
To prevent such behavior I use Meteor.userId() which is always available no matter what. And I only use Meteor.user() when I have first tested that Meteor.userId() returned something and if I need more information about the user.
I would to show a list of all users, in my template.
I have:
//publications.js
Meteor.publish('users', function() {
return Meteor.users.find({}, { fields: {username: 1, profile: 1} });
});
//router.js
Router.route('/users/add/:_id?', {name: 'users.add', controller: 'UserAddController'});
UserAddController = RouteController.extend({
subscriptions: function(){
return [ Meteor.subscribe('hospitals'),
Meteor.subscribe('roles'),
Meteor.subscribe('users') ];
},
action: function() {
this.render('addUser', {
data: function(){
return { hospital_id : this.params._id }
}
});
}
});
//client
Template.listUsers.helpers({
users: function() {
return Meteor.users.find({});
}
});
But the list keep showing only the current logged-in user. I have created a list of users using Account.createUser() function What am I doing wrong?
Thanks.
You have to subscribe to a publication using this.subscribe() in subscriptions hook:
// a place to put your subscriptions
subscriptions: function() {
this.subscribe('items');
// add the subscription to the waitlist
this.subscribe('item', this.params._id).wait();
}
Or use waitOn:
// Subscriptions or other things we want to "wait" on. This also
// automatically uses the loading hook. That's the only difference between
// this option and the subscriptions option above.
waitOn: function () {
return Meteor.subscribe('post', this.params._id);
}
By default, Meteor publishes the current user. I see that you have a addUser template and a listUsers template. The problem is that while addUser is subscribed to the users publication, listUsers is not (this would depend on what else you have in your router of course). To fix this, change the call to this.render to render the listUsers template. Then, your users helper should work, and you can render the information however you like.
I tested this with my own app (the Microscope project from DiscoverMeteor) and it worked for me. Hope it works for you too. Comment here if not, and be sure to accept this answer if it worked. =)
I have a 'profile' template where I will display user related stuffs. So I wanna make a route for the template, but in the 'path' I want to dynamically insert the current user's username. Just the way we dynamically change the url with regard to post's id and everything.
Here's the router code block as of now.
Router.map(function() {
this.route('profile', {
path: '/profile', //here instead of 'profile' I wanna dynamically insert the current user's username.
});
});
By the way, I was able to load the user related data's to the said template.
I tried loading the username(/username) to the route path in a trial and error way, but in vain. :(
I guess I'm not very good with Iron Router after all. Please help.
I too was struggling with this one for a while... then I came across this SO answer. In my case, I was doing everything right except for failing to pass the username along with the template pathFor link helper.
For some reason, when using :_id in iron router routes, there's no need to reference it in the pathFor helper. This was the source of my confusion, perhaps others' as well.
Here is sample code of using the username in a path for iron router:
router.js
this.route('/:username', {
name: "dashboard",
waitOn: function() {
return Meteor.subscribe("allUserData");
},
data: function() {
return Meteor.users.findOne();
}
});
publications.js
Meteor.publish("allUserData", function() {
if (this.userId) {
return Meteor.users.find(this.userId)
} else {
this.ready()
}
})
page.html
<a href="{{pathFor 'dashboard' username=username}}">
User Dashboard
</a>
Again, at least in my particular case, I was missing the above username=username.
Have you tried this?
this.route('profile', {
path: '/:username',
data: function() { return Meteor.user().username; }
});
Use router parameters:
Router.map(function() {
this.route('profile', {
path: '/:_username', //dynamic parameter username
data: function() {
//here you will get the username parameter
var username = this.params.username;
return {
user: Meteor.users.find({ username: username }) //you can use user object in template
};
}
});
});
Don't forget the waitOn property on routes. Most of the time it's just the timing that's off, creating a publication for this is the best way to get rid of that issue..
Server side, publications.js:
Meteor.publish('me', function() {
if(!this.userId) return false;
else return Meteor.users.find({_id: this.userId});
});
In one of your Router.map() routes:
this.route('me', {
template: 'profile',
notFoundTemplate: 'profile_not_found',
path: '/profile',
waitOn: function() {
return Meteor.subscribe("me");
},
data: function() {
return Meteor.user();
}
});
Don't forget these configuration bits as well:
// Router config.. pretty self explanatory
Router.configure({
layoutTemplate: 'main',
notFoundTemplate: 'not_found',
loadingTemplate: 'loading'
});
// handle the loading screen
Router.onBeforeAction('loading');
// make sure you define routes here that rely on data to throw back
// 404/not found equivalent pages. e.g. no search results found,
// or in this case profile not found
Router.onBeforeAction('dataNotFound', {only: ['profile']});
and you can use the profile template:
<template name="profile">
Current user Id: {{_id}}
</template>
<template name="profile_not_found">
Profile not found. Are you logged in?
</template>
I'm trying to access data passed from iron router in the javascript function
router.js
this.route('editOrganization', {
path: '/editOrganization',
waitOn: function() {
return [
Meteor.subscribe('organization', this.userId)
];
},
data: function() {
return Organizations.findOne();
}
});
now if I wanted to access a property of organization in html (editCompany.html) I can do the following
{{name}}
but how do I access that same property in the js file
Template.editOrganization.rendered = function() {
//how do I access name?
}
UPDATE:
so if I click a link to edit organization I can get the value via
this.data.name
However, if I reload the page (same url) it throws an error saying data is null.
It is accessible through the rendered function context.
Template.editOrganization.rendered = function() {
var name = this.data && this.data.name;
};
This is confusing for many people but you need to configure the router to actually wait for the subscriptions you returned with waitOn.
Router.onBeforeAction('loading')
You can read the author's explanation here:
https://github.com/EventedMind/iron-router/issues/554#issuecomment-39002306