Iron router data findOne returning undefined - meteor

I have a streams publication and subscription setup but for some reason if I do the following in my route the view gets no data:
Router.route '/seasons/:season/episodes/:episode',
name: 'episode'
action: ->
#render(
'episode',
data: ->
Streams.findOne({season: #params.season, episode: #params.episode})
)
If I log the params they are there as expected, and doing a findOne manually either via the db or the browser console returns the data as expected.
If I remove the params so it just does Streams.findOne() the data returns the first stream from the database and the view has access to it as expected. I'm really not sure what's going on here.

You probably need to wait on your streams publication before trying to access the data : Pub/Sub mechanism in Meteor is asynchronous, when you subscribe to some data, you don't instantly get it back in the browser because of the underlying client/server latency.
Try reorganizing your code as follow :
Router.route '/seasons/:season/episodes/:episode',
name: 'episode'
template: 'episode'
data: ->
Streams.findOne({season: #params.season, episode: #params.episode})
waitOn: ->
Meteor.subscribe 'streams', #params.season, #params.episode

Related

Meteor Vue Tracker - Unable to Watch Subready. Computed property with subready is not working either

I am trying to move my project from blaze to vue.
I am using meteor 1.6.1 and vue-meteor-tracker.
I had a case where in a component a subscription should take place only when the other subscription is ready.
I wrote the following code and faced the problems mentioned in the comments.
export default
data: ->
filter: null
computed:
subscriptionReady: ->
# this gets called only once and the value does not change even when $subReady.users changes.
#$subReady.users
meteor:
$subscribe:
filter: []
filter: -> Filter.findOne()
watch
'$subReady.filter': (value) ->
console.log('this watch is not working')
if value
#$subscribe('users', #filter)
'$subReady': (value) ->
console.log('this watch is not working either')
'filter': (value) ->
# this will work but i can not use this as I need the below subscription to run even when there is no filter in the db.
if value
#$subscribe('users', #filter)
I can do null handling in publication but in the above case when filter is not present, there is a default value for filter.The problem is when the filter subscription is not ready users data is returned with default filter value. The users subscription gets called again when the filter subscription is ready.
Note: The above case is not exactly the same as I am facing in the project so I apologize if I missed something and the question is not clear.
Any suggestion or help would be appreciated. Thanks in advance.

$http returning error response NULL on first call after launch (ionic) everytime, but after subsequent http post its ok

Whenever I launch my app, and click on login on the first few tries, the login will attempt a POST http to the server. However $http always (everytime) returns NULL on first try. sometimes after several few tries still NULL if done fast. But subsequently, its all ok.
I dont get it, why is $http returning error response NULL initially ??
Here is my login controller doing the http post
Login Controller (LoginCtrl)
https://gist.github.com/anonymous/771194bc5815e4ccdf38b57d6158853f
var req = {
method: 'POST',
url: baseURL,
data: postObject,
//timeout: 5000
};
err is NULL here:
}).error(function(err) {
I dont know if it is CORS but I'ved got this set in config.xml
<access origin="*" />
my config.xml
https://gist.github.com/anonymous/b2df3a857338d14ec3fcd6dda776e212
Any ideas ?
Im using ionic 1.7.14
on device iOS 9.3.1
UPDATE
I'ved put the problem code here. can logout first to goto login screen. enter in anything in username/password field, click login once failed, second or third try will be success.
https://github.com/axilaris/ionic_null_http_problem
some troubleshooting so far: i noticed the http post request is called twice. not sure why.
UPDATED the code using $http.post.then but still has the same effect
$http.post(baseURL, postObject).then(function successCallback(response)
response has NULL data --> Object {data: null, status: 0, config: Object, statusText: ""}
It is hard to diagnose having the above details only.
However the problem could be that your handler (login function) is triggered before digest cycle finished updating $scope.data.username and $scope.data.password and for the first tries it sends empty values for those to the server and works fine later.
You can run Safari web inspector to see what is sent to the server to prove this.
The fix may depend on how your view/template is coded. Can you please share it? Or, ideally, create a working sample at http://play.ionic.io/
Another option to fix could be to try to wrap your code related to http request into
$timeout(function() {
// your code goes here
});
or, consider using .$applyAsync() (see the docs for details)
This might help to fix the problem
You are probably getting this inconsistent behavior as you are using the 'success' promise method instead of 'then' (note that use of the success method has now been deprecated).
The key differences between these two methods are:
then() - full power of the promise API but slightly more verbose
success() - doesn't return a promise but offeres slightly more convienient syntax
as highlighted in this answer.
Hence in your scenario, instead of using 'success':
var req = {
method: 'POST',
url: baseURL + 'session/login',
data: postObject,
//timeout: 5000
};
$http(req).success(function(resp) {...
use 'then' along with angular's post shortcut method (you don't have to use this shortcut method, but I think it makes the code more succinct) e.g.:
$http.post(baseURL + 'session/login', postObject).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Using 'then' returns a promise resolved with a value returned from a callback, so it should give you a consistently valid result.
it was a timeout in app.js that caused it. was set to 1 second which gives it it arbitrary success rate.
config.timeout = 1000;

Meteor Velocity Jasmine: Prevent beforeEach Global Scope Across Describes

In my Meteor app, I have a form at /join with a disabled button. I test this disabled state with the following integration test file:
// tests/jasmine/client/integration/user/joinSpec.js.coffee
describe 'user', ->
describe 'join', ->
beforeEach ->
Router.go 'join_path'
it 'is unsubmittable by default', ->
expect($('#join-submit')).toHaveAttr 'disabled', 'disabled'
I also have a form at /signup with a disabled button. I test that disabled state with this additional integration test file:
// tests/jasmine/client/integration/user/signupSpec.js.coffee
describe 'user', ->
describe 'signup', ->
beforeEach ->
Router.go 'signup_path'
it 'is unsubmittable by default', ->
expect($('#signup-submit')).toHaveAttr 'disabled', 'disabled'
Both tests pass independently of each other (i.e. when only one file exists). However, the user.signup test only passes in absense of the user.join test, I assume due to the way Meteor executes the files in order by filename.
It seems like beforeEach has global scope, and the one from user.join is overriding the one in user.signup, causing the sign up test to execute on the wrong route and fail. Any idea why this is, or how to lock it down to local scope?
Thanks!
The problem was Iron Router. Refer to the following article for resolution: https://meteor-testing.readme.io/docs/jasmine-integration-tests-with-iron-router

Meteor 1.0 - Hosting a lobby and redirecting other users

I have a problem for which I do not know the solution. I am creating game that involves multiple players. One person "hosts" the game and sends "invitations" to other users to join the "lobby". If you've ever played Call of Duty or any similar game, it's the same concept.
Much of this process currently works properly.
I created a collection called Lobby to keep track of all the open and closed lobbies. When a user wants to host a lobby, he clicks on the button, which creates a new lobby and redirects the user to the proper url:
Template.lobby.events
'click button#host-lobby': (e,t) ->
lobbyId = Lobby.insert
host: Meteor.user()._id
hostName: Meteor.user().username
status: true
players: []
Router.go("currentLobby", _id: lobbyId)
Then, the user can invite other users to the lobby (url) through a modal, which adds a notification object to the invited user's profile. Probably not the best way to do it, so I'm open to suggestions on this front.
Template._playerItem.events
'click button': (e,t) ->
lobbyId = Session.get "currentLobby"
Meteor.call "sendNotification", #_id, lobbyId, Meteor.user().username
Lobby.update
_id: lobbyId
,
$addToSet:
invitedPlayers: #_id
And the method:
Meteor.methods
sendNotification: (userId, lobbyId, hostName) ->
sendTo = Meteor.users.findOne(_id: userId)
Meteor.users.update
_id: userId
,
$push:
invite:
hostName: hostName
lobbyId: lobbyId
So at this point, the user can either accept or decline the invitation. If he accepts, he is routed to the lobby and is added to the players array in the lobby object. The user shows up in the list of players as one would expect.
My issue starts when I attempt to "start" the game. When the button is clicked, the game is created properly and the host (who pressed the button) is routed to the url for the new game:
Template.currentLobby.events
'click #start-game': (e,t) ->
playerIds = [#host]
#players.forEach (player) ->
playerIds.push(player.id)
Meteor.call 'createGame', playerIds
Router.go('home')
The problem is that the other users in the lobby are not redirected. They have access to the game if they manually go to the url, but they aren't taken there. They have no way of knowing that the game has actually started...
One solution is to add a "Game has started" badge, with a link to the game, but I think a more elegant solution is to route all of the users at the current lobby url to the game that was just started.
Is this functionality possible? Is there a better way to host a lobby?
EDIT
Thank you Chet for the solution. This is how I eventually implemented it:
Template.currentLobby.rendered = ->
#autorun ->
data = Template.currentData()
if data.url
Router.go data.url
#autorun had some context difficulties, so I just used the lobby data. Then, when someone clicks on the "start game" button, the current lobby is updated with the url of the new game (The Meteor.call 'createGame' returns the _id of the new game).
Template.currentLobby.events
'click #start-game': (e,t) ->
playerIds = [#host]
lobbyId = Template.currentData()._id
#players.forEach (player) ->
playerIds.push(player.id)
Meteor.call 'createGame', playerIds, (err, res) ->
Lobby.update
_id: lobbyId
,
$set:
url: "/game/#{res}"
Works like a charm. Thanks!
OK. First, to address your notifications. This is a valid solution. However, you'll probably want to sort them by date! You'll also want to create a separate Notifications collection. Anytime you update a document, the whole thing is send over DDP to the client. Thus any new notification added to the profile will result the entire profile being sent to the client. You'll also want some way of marking a notification as being read so it can be deleted.
To address you question about redirecting the lobby, create property called url. Initially it is set to false. Once the host is ready to start the game, they set the url property to the url.
When a user enters a lobby, start an autorun for the redirect.
Template.lobby.rendered = ->
#autorun ->
if game.url
Router.go(game.url)
You'll have to make sure that the game.url is a reactive datasource. If you pass the game as the data context using iron router, then you should be able to use #data.url but I'm not 100% sure it will be reactive. Just to be safe, try Games.findOne(#data._id) -- that will certainly be reactive.
EDIT:
Just to be clear, try this:
Template.lobby.rendered = ->
#autorun ->
game = Games.findOne(#data._id)
if game.url
Router.go(game.url)

Can I create an index in a mongo collection with Meteor? [duplicate]

I am trying to create a two column unique index on the underlying mongodb in a meteor app and having trouble. I can't find anything in the meteor docs. I have tried from the chrome console. I have tried from term and even tried to point mongod at the /db/ dir inside .meteor . I have tried
Collection.ensureIndex({first_id: 1, another_id: 1}, {unique: true}); variations.
I want to be able to prevent duplicate entries on a meteor app mongo collection.
Wondering if anyone has figured this out?
I answered my own question, what a noob.
I figured it out.
Start meteor server
Open 2nd terminal and type meteor mongo
Then create your index...for example I did these for records of thumbsup and thumbsdown type system.
db.thumbsup.ensureIndex({item_id: 1, user_id: 1}, {unique: true})
db.thumbsdown.ensureIndex({item_id: 1, user_id: 1}, {unique: true})
Now, just gotta figure out a bootstrap install setup that creates these when pushed to prod instead of manually.
Collection._ensureIndex(index, options)
Searching inside Meteor source code, I found a bind to ensureIndex called _ensureIndex.
For single-key basic indexes you can follow the example of packages/accounts-base/accounts_server.js that forces unique usernames on Meteor:
Meteor.users._ensureIndex('username', {unique: 1, sparse: 1});
For multi-key "compound" indexes:
Collection._ensureIndex({first_id:1, another_id:1}, {unique: 1});
The previous code, when placed on the server side, ensures that indexes are set.
Warning
Notice _ensureIndex implementation warning:
We'll actually design an index API later. For now, we just pass
through to Mongo's, but make it synchronous.
According to the docs "Minimongo currently doesn't have indexes. This will come soon." And looking at the methods available on a Collection, there's no ensureIndex.
You can run meteor mongo for a mongo shell and enable the indexes server-side, but the Collection object still won't know about them. So the app will let you add multiple instances to the Collection cache, while on the server-side the additional inserts will fail silently (errors get written to the output). When you do a hard page refresh, the app will re-sync with server
So your best bet for now is probably to do something like:
var count = MyCollection.find({first_id: 'foo', another_id: 'bar'}).count()
if (count === 0)
MyCollection.insert({first_id: 'foo', another_id: 'bar'});
Which is obviously not ideal, but works ok. You could also enable indexing in mongodb on the server, so even in the case of a race condition you won't actually get duplicate records.
The Smartpackage aldeed:collection2 supports unique indices, as well as schema-validation. Validation will both occure on server and client (reactivly), so you can react on errors on the client.
Actually why not use upsert on the server with a Meteor.method and you could also send also track it with a ts:
// Server Only
Meteor.methods({
add_only_once = function(id1,id2){
SomeCollection.update(
{first_id:id1,another_id:id2},{$set:{ts:Date.now()}},{upsert:True});
}
});
// Client
Meteor.call('add_only_once',doc1._id, doc2._id);
// actual code running on server
if(Meteor.is_server) {
Meteor.methods({
register_code: function (key,monitor) {
Codes.update({key:key},{$set:{ts:Date.now()}},{upsert:true});
}
...

Resources