Meteor: Why is Iron Router's onBeforeAction called multiple times? - meteor

When I visit a route in my browser I expect Iron Router to call onBeforeAction once before loading the route. But it looks like it is being called 3 times when first loading the route.
This is an issue for me because I want to put code that redirects the user if they do not have access to that document.
onBeforeAction: ->
console.log 'called once' #Called 3 times when first loading the route!
thread = Research.findOne(#params._id)
console.log thread # This is undefined at first run
thread = Research.findOne(#params._id)
if thread?
Meteor.subscribe 'collabUsers', #params._id
else
Router.go '/research'
Since it is called multiple times, it causes issues with redirecting. First users that do have access are also redirected because at first thread is undefined.
What I am trying to do if check if the user has access to the data the route depends on, if not then I need to redirect the user. So that is why in onBeforeAction I am trying to pull a document from database and if it exists then I will load the page or else I will redirect or throw and error message at the user.
But I notice that the console.log statement in onBeforeAction is called 3 times when the route is first loaded. And also on the first run the user does not have access to any research threads for some reason (I believe the subscription has not been setup and documents are not accessible on first run) So that causes issues with my trying to see if they have access to the document because on first run nobody has access.
Here is the entire router.coffee code
appendUserData = (array) ->
_.each array, (item) ->
user = Meteor.users.findOne(item.userId)
if user?.profile.firstName? && user?.profile.lastName?
item.firstName = user.profile.firstName
item.lastName = user.profile.lastName
item.username = user.username
userEnabled = () ->
if Meteor.user()
if $(window).width() > 768
if !$('.show-left').hasClass 'active'
Meteor.defer ->
$('.show-left').click()
requireLogin = (pause) ->
if !Meteor.user()
#setLayout 'layoutLoggedOut'
if Meteor.loggingIn()
#render #loadingTemplate
else
#render 'login'
pause()
else
#setLayout 'layout'
if window.location.pathname is '/' or undefined
Router.go('addAndSearchPosts')
else
Router.go(window.location.pathname)
Router.configure
layoutTemplate: 'layout'
loadingTemplate: 'loading'
onBeforeAction: ->
#this code get which ids we need to get data from to render template. Here we need to get data to notification of collab
params = {}
params.threadIds = []
params.userIds = []
notifications = Notifications.find {userId: Meteor.userId()}
notifications.forEach (notification) ->
params.threadIds.push notification.threadId
params.userIds.push notification.requesterId
#subscribe('notificationDataCollab', params).wait()
waitOn: ->
[
Meteor.subscribe 'myResearch', Meteor.userId()
Meteor.subscribe "notifications"
]
Router.onAfterAction userEnabled
Router.onBeforeAction requireLogin,
except: 'template1'
Router.onBeforeAction 'loading'
Router.onBeforeAction ->
Errors.clearSeen()
Router.map ->
#route 'posts_page',
path: '/posts',
#route 'template1',
path: '/template1',
#route 'login',
path: '/',
#route 'addAndSearchPosts',
path: '/bookmarks',
waitOn: ->
Meteor.subscribe 'myBookmarks', Meteor.userId()
data: ->
bookmarks: Bookmarks.find
_userId: Meteor.userId()
#route 'research',
path: '/research/:_id',
waitOn: ->
[
Meteor.subscribe 'threadNotes', #params._id, Meteor.userId()
Meteor.subscribe 'collabUsers', #params._id
]
onBeforeAction: ->
console.log 'called once'
#Meteor.subscribe 'collabUsers', #params._id
# thread = Research.findOne(#params._id)
# console.log thread
#thread = Research.findOne(#params._id)
# if thread?
# Meteor.subscribe 'collabUsers', #params._id
# else
# Router.go '/research'
#Errors.throw('Thread does not exist or you do not have access', false)
data: ->
# this code is for appending the user data to pending and current collaborators for this thread
thread = Research.findOne(#params._id)
if thread?.pending?.collaborators?.length > 0
appendUserData(thread.pending.collaborators)
if thread?.collaborators?.length > 0
appendUserData(thread.collaborators)
data =
researchThread: thread,
notes: Notes.find
_threadId: #params._id
,
sort:
date: -1
data
#route 'researchHome',
path: '/research'
#route 'profileEdit',
path: '/editprofile'
Here is publications.coffee
Meteor.publish 'myResearch', (id) ->
Research.find({$or: [{_userId: id}, {'collaborators.userId': id}] })
Meteor.publish 'threadNotes', (threadId) ->
Notes.find({_threadId: threadId})
Meteor.publish 'myBookmarks', (userId) ->
Bookmarks.find({_userId: userId})
Meteor.publish 'collabUsers', (threadId) ->
Meteor.users.find({}, {fields: {profile: 1, username: 1}})
Meteor.publish 'notifications', ->
Notifications.find()
Meteor.publish 'notificationDataCollab', (params) ->
[
Meteor.users.find({_id: {$in: params.userIds}}, {fields: {username: 1}})
Research.find({_id: {$in: params.threadIds}}, {fields: {name: 1}})
]
Any advice on how to handle this is appreciated.

onBeforeAction is run and rerun reactively. You probably want onRun.

Related

Meteor: I can't get the onError callback to fire when throwing error from subscription

Ok this is the server code where I publish my data if the current user have access to see it
# server publish
Meteor.publish 'clients', (locationId) ->
if SecurityContext.canAccessLocation #userId, locationId
#ready()
#error new Meteor.Error('unauthorized', 'You do not have access to this location')
return
ClientManagement.Collections.Clients.find({ locationId: locationId }, { sort: firstName: 1 })
This is a section of my iron-router's controller where I wait for my data to come back but my callbacks for onReady or onError are never called
# iron route controller
waitOn: ->
Meteor.subscribe 'clients', Session.get 'selectedLocationId',
onReady: ->
debugger
onError: (error) ->
debugger
AppHelper.logger.error error.reason
What am I doing wrong here? Any suggestions? I also tried something similar outside of iron-router just to double check it was not related to the router.
I did this on the client side:
Meteor.startup () ->
Tracker.autorun ->
Meteor.subscribe 'clients', Session.get 'selectedLocationId',
onReady: ->
debugger
onError: (error) ->
debugger
AppHelper.logger.error error.reason
Again nothing my callbacks are never called ... any ideas??
Thanks in advance!
This looks like a coffeescript mistake. The transpiler doesn't know if you mean to include the callback object as an argument to subscribe or to get. It chooses the latter, but you want the former. Try something like this instead:
Meteor.subscribe 'clients', Session.get('selectedLocationId'),
onReady: ->
console.log 'ready!'
onError: ->
console.log 'error!'

How can I access iron:router params from an event?

Router.map ->
#route '/foo'
Template.foo.events
'click something': ->
Router.current().params #is empty array
Router.go '/foo', thenGoTo: 'bar'
I want to access the thenGoTo: 'bar' object.
I could do Router.go 'foo', {} {query: 'thenGoTo=bar'} and then parse Router.current().url, but I'd rather keep a clean URL.

Iron Router: My Subscription Doesn't Work Inside waitOn

I have a simple subscription running in Iron Router.
#route 'AdminEditor',
path: '/admin/editor/:id?'
controller: 'AdminController'
template: 'AdminRoot'
yieldTemplates:
'AdminEditor': { to: 'adminAside' }
waitOn: ->
return Meteor.subscribe 'getArticle', #params.id
The publication:
Meteor.publish 'getArticle', (id) ->
return Articles.find { 'id': id }
My database is not empty and have a single document into it:
db.articles.find().pretty()
{
"authors" : [
"dqbxgrxzehXXTbFgG"
],
"date" : ISODate("2014-10-01T20:07:48.846Z"),
"title" : "Meteor #5",
"content" : "hello",
"id" : 1,
"_id" : "WL7ygMw2jL9WnmRYZ"
}
But when I go to /admin/editor/1 and type in my chrome debugger Articles.findOne(), it doesn't return anything. I also have defined the collection in a lib folder:
#Articles = new Meteor.Collection 'articles' # run on both client and server
I know there is probably a little thing I forgot, but the subscription seems to work: when I subscribe to it from the chrome debugger and do a find, it returns the object.
Meteor.subscribe('getArticle', 1);
Articles.findOne();
Can you log the value of #params.id in the waitOn to check what you're actually sending? It could be the fact that you're sending it as a string rather than an integer (as you are in the console). If so, you just need to parseInt and it should work.

Stuck on loading template

Hey I have a system where a logged in user can be in a "battle" and as soon as they are, I want the user to be locked on a given template, until the battle is done.
I have
#
# Currently active battle
#
Meteor.publish 'activeBattle', ->
character = Characters.findOne(userId: this.userId)
if this.userId and character
return Battles.find({active: true, $or: [{characterOneId: character._id}, {characterTwoId: character._id}]})
else
return
and in my iron-router
Router.configure
layoutTemplate: 'layout'
loadingTemplate: 'loading'
waitOn: [
Meteor.subscribe('activeBattle')
]
...
redirectToActiveBattle = (pause) ->
battle = Battles.findOne(active: true)
if battle and Meteor.userId()
throwError('You have a battle in progress.')
Router.go('combat', {_id: battle._id})
pause()
...
Router.onBeforeAction(redirectToActiveBattle, except: ['login', 'logout', 'signup', 'combat'])
This works when the user is logged in and has a character, but if not, the page is stuck on the loading template, instead of displaying the login screen
Actually, you should use this.ready(); instead of an empty return if you don't want to return anything in a publication. So the right way to do the publication could be:
Meteor.publish 'activeBattle', ->
character = Characters.findOne(userId: this.userId)
if this.userId and character
return Battles.find({active: true, $or: [{characterOneId: character._id}, {characterTwoId: character._id}]})
this.ready()

Meteor - How can I limit which fields are published to the client?

I want to publish only a limited amount of data to the client.
I've tried to do it like this:
# server
Meteor.publish('users', ->
Meteor.users.find({},
fields:
services: 0
)
)
But the client still receives the whole object.
# client
Meteor.startup( ->
Meteor.subscribe('users')
)
# ...
# in another function
Meteor.users.find().observe( ->
changed: (updated) ->
console.log updated
)
What am I doing wrong?
Meteor.publish '', ->
Posts.find({}, { fields: { title: 1, content: true, secret: false } });
what about add those {}
The code below works for me (coffeescript). The pwd field isn't published.
Server
Meteor.publish "users", (userId) ->
user = Users.find userId,
fields:
pwd: false
return user
Client
Meteor.autosubscribe ->
userId = Session.get SESSION_USER
Meteor.subscribe 'users', userId
The only differences I see are
0 vs false... (should be a matter of taste, only)
Your collection is accessed via Meteor
In the client my subscription is placed inside a autosubscribe callback while you're using the observe method.
Do the fields exists in the result of Meteor.users.find().fetch() in the browsers console, too?

Resources