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

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.

Related

Implementing a simple search with Meteor and Iron Router

In the next phase of my Meteor journey (read: learning the ropes!), I'd like to implement a simple search based on user inputed values, then redirect to a route specific to the record returned from the server.
At the moment, I'm picking up the inputed values via this code:
Template.home.events 'submit form': (event, template) ->
event.preventDefault()
console.log 'form submitted!'
countryFirst = event.target.firstCountrySearch.value
countrySecond = event.target.secondCountrySearch.value
Session.set 'countryPairSearchInputs', [countryFirst, countrySecond]
countryPairSearchInputs = Session.get 'countryPairSearchInputs'
console.log(countryPairSearchInputs)
return Router.go('explore')
Happily, the console log returns the desired countryPairSearchInputs variable - an array of two ids. In my routes.coffee file I then have the following:
#route "explore",
path: "/explore/:_id"
waitOn: ->
Meteor.subscribe 'countryPairsSearch'
On the server side, I have:
Meteor.publish 'countryPairsSearch', getCountryPairsSearch
And finally, I have a search.coffee file in my /lib directory that defines the getCountryPairsSearch function:
#getCountryPairsSearch = ->
CountryPairs.findOne $and: [
{ country_a_id: $in: Session.get('countryPairSearchInputs') }
{ country_b_id: $in: Session.get('countryPairSearchInputs') }
]
With regards to the search function itself, I have a CountryPairs collection where each record has two ids (country_a_id and country_b_id) - the aim here is to allow users to input two countries, with the corresponding CountryPair then being returning.
I'm currently struggling to tie all the pieces together - the console output on searching is currently:
Uncaught Error: Missing required parameters on path "/explore/:_id". The missing params are: ["_id"]. The params object passed in was: undefined.
Any help would be greatly appreciated - as you can probably tell I'm new to Meteor and am still getting used to the pub/sub methodology!
Edited: mixed up client/server for the publish method when I first posted - the danger of late-night posting!
First, seems you're expecting an :id parameter on your 'explore' route.
If I understand you're case, you're not expecting any params here, so you can just delete ':id' from your route:
#route "explore",
path: "/explore/"
waitOn: ->
Meteor.subscribe 'countryPairsSearch'
or either add a params to your Router.go call:
Router.go('explore', {_id: yourIdVar});
Secondly, you're trying to use a client function: Session.get() server-side. Try to update the publication using a parameter ; or using a method.call.
client-side
Meteor.subscribe 'countryPairsSearch' countryA countryB
not sure about the coffeescript syntax, check http://docs.meteor.com/#/full/meteor_subscribe
and server-side
#getCountryPairsSearch = (countryA, countryB) ->
CountryPairs.findOne $and: [
{ country_a_id: $in: countryA }
{ country_b_id: $in: countryB }
]

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!'

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.

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

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.

meteor iron-router session.get doesn't work

I'm trying to implement this route:
#route "buildingSpaceTenant",
path: "/buildings/:_building_id/spaces/:_space_id/tenants/:_id/communications"
template: "tenant"
yieldTemplates: {
Session.get("current_tenant_subtemplate"): {to: "subTemplate"}
}
but apparently I can't use the session object this way.
<runJavaScript-30>:148:11: client/routes/tenantsRoute.coffee:44: unexpected . (compiling client/routes/tenantsRoute.coffee) (at handler)
What's the right way?
You can't use variable names as keys in an object literal. If yieldTemplates can accept a function (I don't think it can), you could try something like:
#route 'buildingSpaceTenant',
path: '/buildings/:_building_id/spaces/:_space_id/tenants/:_id/communications'
template: 'tenant'
yieldTemplates: ->
templateName = Session.get 'current_tenant_subtemplate'
obj = {}
obj[templateName] = to: 'subTemplate'
obj
I had a look at the example from this issue which implies that you could try overriding action to achieve the same end result.
#route 'buildingSpaceTenant',
path: '/buildings/:_building_id/spaces/:_space_id/tenants/:_id/communications'
template: 'tenant'
action: ->
if #ready()
#render()
templateName = Session.get 'current_tenant_subtemplate'
#render templateName, to: 'subTemplate'
else
#render 'loading'
Give those ideas a try and let me know how it goes.

Resources