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()
Related
I have a Sessions.get() in my MongoDB query. This means the displayed collection depends on the Sessions.get() value. To give you a clearer image of what I mean, find below what my query looks like:
Template.paymentB2C.onCreated( function(){
Session.set('pendingFilter', [ "Pending", "Failed", "Success" ]);
});
.
Template.paymentB2C.helpers({
'showTransactions': function(){
var transactions = paymentDetails.find({
paymentStatus:{$in: Session.get('Filter')}
}).fetch();
return transactions;
}
});
The above code displays a list of transactions. The displayed list of transactions varies in types of transactions suggesting: Failed, Successful, to Pending transactions as seen more accurately below:
0:
payersName: "Sa Acea"
paymentStatus: "Success"
_id: "D3gY4BoR2tvxdK4xv"
1:
payersName: "Paul Acea"
paymentStatus: "Pending"
_id: "ajCjYbLaDP7x4iAFK"
2:
payersName: "Simon Bolton"
paymentStatus: "Success"
_id: "K4d6wDrjRRdSyCkhW"
I, therefore, use the Filter Session values as a Filter to dictate what types of transactions to display.
I am able to interchange between the three types of Filters via the below events. Find below the code.
Template.paymentB2C.events({
'click .ShowFailedTransactions' (event, instance) {
event.preventDefault();
Session.set('Filter', [ "Failed" ]);
},
'click .ShowSuccessfulTransactions' (event, instance) {
event.preventDefault();
Session.set('Filter', [ "Success" ]);
},
'click .ShowPendingTransactions' (event, instance) {
event.preventDefault();
Session.set('Filter', [ "Pending" ]);
},
});
Note that there isn't a Failed transaction type in the collection. So I find it very strange that whenever I choose the Failed filter 'click .ShowFailedTransactions' expecting the desired effect to be a blank empty page, the page fails to update and continues to the show the results of the previous query, as if I never clicked 'click .ShowFailedTransactions'.
I thought perhaps the Session.set('Filter', [ "Failed" ]) in the 'click .ShowFailedTransactions' function wasnt working, so I added a console.log(); to print out the Filter Session value as seen below:
'click .ShowFailedTransactions' (event, instance) {
event.preventDefault();
Session.set('Filter', [ "Failed" ]);
var displaySession = Session.get('Filter');
console.log("Filter Value is: " +displaySession );
},
The above code yields Filter Value is: Failed in the browser console, suggesting that the Filter session value gets updated as expected.
I am confused, to why the page will not respond/update to this.
Kindly help suggest a solution for this issue.
The problem in your code is the fetch, it's not reactive! The good news is that you don't need it. Try this:
Template.paymentB2C.helpers({
'showTransactions': function(){
return paymentDetails.find({paymentStatus: {$in: Session.get('Filter')}});
}
});
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!'
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.
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.
I have a simple list and details view using two collections.
When I navigate back to the list view Meteor removes the single document added to the details collection and undoes the change to the other collection.
I want this data to remain so the client doesn't have to keep reloading it...
Both the 'league' and the 'standings' subscriptions are 'undone' on navigation back to the the root. The league and leagues route both use the 'weeks' Mongo collection. When navigating to a league detail I add to the single document. Navigation to the detail works fine ... its when I navigate back to the list that I loose the collection data.
I need all this data 'cached' and am obviously not going about it correctly....
Router.map(function () {
this.route('leagueList', {
path: '/'
});
this.route('league', {
path: '/league/:league',
template: 'standings',
waitOn: function () {
console.log(this.params.league);
return [Meteor.subscribe('league', this.params.league),
Meteor.subscribe('standings', this.params.league) ];
},
data: function () {
return {theLeague: Leagues.findOne({league: this.params.league}),
theStandings: Standings.findOne()};
}
});
});
Server:
Meteor.publish('leagues', function(){
console.log('all league names sent');
return Leagues.find({}, {fields: {weeks: 0}});
});
Meteor.publish('league', function(theLeague){
console.log('sending weeks detail for: ' + theLeague);
return Leagues.find({league: theLeague});
});
Meteor.publish('standings', function(theLeague){
console.log('standings: ' + theLeague);
var file = Leagues.findOne({league: theLeague}).weeks[0].file;
return Standings.find({file: file});
});
client:
Leagues = new Meteor.Collection('weeks');
Standings = new Meteor.Collection('details');
Meteor.subscribe('leagues');
There's work in progress in iron router to allow (and optimize) this (not immediately stopping the subscriptions when you route to another page). See the sub-manager branch.
But if you create the subscription apart from the waitOn call, I think the subscription is never stopped. For example, in the code below, the routes a and c will wait for the initialData to be received (which will be fetched directly when the user loads the page (even if it uses route b)), and the subscription for it will never stop, even if you leave, for example, route a. However, I don't think you can use this approach if you need to use some parameters in the route (you can probably fix something with setInterval, but it will be ugly).
var handleToDataIMostlyNeed = Meteor.subscribe('initialData')
Router.map(function(){
this.route('a', {
waitOn: function(){
return handleToDataIMostlyNeed
}
})
this.route('b', {
waitOn: function(){
return [] // Wait for nothing.
}
})
this.route('c', {
waitOn: function(){
return handleToDataIMostlyNeed
}
})
})