I have this simple Iron-router route in my Meteor application which I use whenever any user need to logout of the app, although whenever I call this route I get the following error. Can someone please tell me what I am doing wrong/ missing here? Thanks
On a minor note, Meteor.logout doesn't return any error
Router.route('/logout', function(){
Meteor.logout(function(err){
if(err){
console.log('Error Logging out: '+ err);
}
this.redirect('home');
});
});
Error:
Route dispatch never rendered. Did you forget to call this.next() in an onBeforeAction?
Exception in delivering result of invoking 'logout': TypeError: undefined is not a function
at http://localhost:3000/both/router/routes.js?8871acf5e06150f7af89862f68c245a05fe13db8:110:12
at http://localhost:3000/packages/accounts-base.js?7c29db5c21a76726509bb6bb2a68a2b4b1ecf657:674:19
Scope in Meteor.logout callback is not the same scope as in Router.route callback. That's why you need to assign Router.route's scope to variable self and then use it inside Meteor.logout callback.
Router.route('/logout', function(){
var self = this;
Meteor.logout(function(err){
if(err){
console.log('Error Logging out: '+ err);
}
self.redirect('home');
});
});
Related
This Meteor server code tries to use Meteor.userId() in public method "sendEmail", but sometimes I get the error
Error Meteor.userId can only be invoked in method calls. Use this.userId
lib = (function () {
return Object.freeze({
'sendEmail': function(msg){
let userId = Meteor.userId();
//do stuff for this user
},
'otherPublicMethod': function(){
//do other things then use sendEmail
lib.sendEmail(); // <---- Error Meteor.userId can only be invoked in method calls. Use this.userId
}
});
}());
// Now I call sendEmail from any where, or can I?
Meteor.methods({
'sendEmail': (msg) => {
lib.sendEmail(msg); // <---- NO error when this is called
},
});
How can it be fixed? thx
i'm going to gently suggest you replace your use of IIFE. instead, you can take advantage of ES16 modules to define your common functions.
as you've indicated, Meteor.userId() is available in method calls, but won't be available in standalone functions on the server. the pattern i use, when invoking such functions from a method call, is to pass in the userId (or actual user). e.g.
imports/api/email/server/utils/emailUtils.js:
const SendEmail = function(userId, msg) {
// do stuff
};
export {SendEmail};
imports/api/email/server/emailMethods.js:
import {SendEmail} from '/imports/api/email/server/utils/emailUtils';
Meteor.methods({
'sendEmail': (msg) => {
check(msg, String);
// other security checks, like user authorization for sending email
SendEmail(Meteor.userId(), msg);
},
});
now, you have a re-usable SendEmail function you can call from any method or publish. additionally, by following this pattern, you're one step closer to creating testable code. i.e. it's easier to test a function into which you're injecting a userId than it is to mock "this.userId" or "Meteor.userId()".
If lib.sendEmail is being called from any async method, then ensure that you bind Meteor environment
e.g. check code below which simulates the async behaviour
lib = (function () {
return Object.freeze({
'sendEmail': function (msg) {
let userId = Meteor.userId();
console.log(userId);
//do stuff for this user
},
'otherPublicMethod': function () {
//do other things then use sendEmail
lib.sendEmail(); // <---- Error Meteor.userId can only be invoked in method calls. Use this.userId
}
});
}());
// Now I call sendEmail from any where, or can I?
Meteor.methods({
'sendEmail': (msg) => {
//simulate async behaviour + bind environment
Meteor.setTimeout(Meteor.bindEnvironment(function () {
lib.sendEmail(msg); // <---- NO error when this is called
}));
//output :
// null - if user has not logged in else
// actual userId - if user is loggedin
//simulate async behaviour without binding environment
Meteor.setTimeout(function () {
lib.sendEmail(msg); // <---- error when this is called
});
//output :
// Exception in setTimeout callback: Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
},
});
I'm trying to understand why am I getting the error when calling a meteor server method. It works on the server side but it's throwing errors in the browser.
This is my server code in /server/methods.js file:
Meteor.methods({
getTicketSettings: function(){
var getTicketConfig = function(callback){
Assets.getText('ticketCustomizing.json', function(error, res){
if (error)
throw new Meteor.Error({error:'ticket-getCustomizing', reason:'No se pudo recuperar la configuraciĆ³n.'});
else callback && callback(null, JSON.parse(res));
});
}
var syncAssetRetrieve = Meteor.wrapAsync(getTicketConfig);
var result = syncAssetRetrieve();
return result;
},
});
And this is in my client/server code in /lib/initialization.js file:
App.config.tickets.tipos = new Mongo.Collection('tipos');
Meteor.startup(function(){
moment.locale('es');
var ticketSettingsObj = Meteor.call('getTicketSettings');
console.log(ticketSettingsObj);
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
When I run my application I have the JSON object logged in the console but the browser is showing this error: Uncaught TypeError: Cannot read property 'tipos' of undefined in my /lib/initialization.js here:
_.map(ticketSettingsObj.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
Obviously I misunderstood something but still wondering...
You need to pass a callback to the Meteor.call. The server can run it synchronously, blocking until it gets a return, but the client cannot so ticketSettingsObj will always be undefined.
See Meteor docs
Without error handling (and untested):
Meteor.call('getTicketSettings', function(error, result){
console.log(result);
_.map(result.tipos, function(tipo){
App.config.tickets.tipos.insert(tipo);
});
});
Do a console.log(App.config.tickets) and see if it returns a valid object. If it doesn't then you have defined the object App.config.tickets only on server side. If this is intentional and you only want this to be accessible on server side then then add a if(Meteor.isServer) or move the definition it to a file inside /server directory.
I am trying to consume a REST API in the meteor application. Inside the server.js file which is in the server folder, I have written this code:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
Inside the client.js file, which is in the client folder, I have written down this code:
Meteor.call("checkTwitter", function(error, results) {
console.log(results.content); //results.data should be a JSON object
});
I get this error message in console:
"Exception while simulating the effect of invoking 'checkTwitter' Error: Can't make a blocking HTTP call from the client; callback required".
I have the callback function defined in client due to which I don't understand this error. What is it that I am doing wrong?
Meteor.http has been deprecated, please see the HTTP package.
I think that since there's a stub, "checkTwitter" will actually also run on the client. Once the server returns, its result will overwrite the result from teh client run.
In this case, since Meteor.http.call can't run on the client without a callback, you get the error.
Try changing:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
With
Meteor.methods({
checkTwitter: function () {
if (Meteor.isServer) {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
}
});
I just created two routes that work just fine, but I'm getting an odd error in the console that I would like to fix.
Exception in callback of async function: TypeError: object is not a function
at OnBeforeActions.loginRequired (http://localhost:3000/client/router/config.js?8cea1a53d7ab131377c2c4f91d534123cba79b70:12:20)
This error shows up every time I visit the same page.
This is my config.js file:
Router.configure({
layoutTemplate: "uMain"
});
var OnBeforeActions = {
loginRequired: function (pause) {
"use strict";
if (!Meteor.userId()) {
this.render("uLogin");
return pause();
} else {
this.next();
}
}
};
Router.onBeforeAction(OnBeforeActions.loginRequired, {
except: ["uLogin"]
});
The idea is to redirected all user who are not logged in to "uLogin".
It works (or I haven't found any bugs so far).
What am I doing wrong?
You can see the line where you have the error in developers console when you click on link http://localhost:3000/client/router/config.js?8cea1a53d7ab131377c2c4f91d534123cba79b70:12:20 in your console.
Your problem is that new Iron Router does not use pause() anymore. Remove pause from your onBeforeAction.
Developers console is your good friend. Learn how to use it.
I have the following controller that uses AngularFire
app.controller("authController", function($scope, $firebaseSimpleLogin){
var ref = new Firebase("https://myapp.firebaseIO.com/");
$scope.auth = $firebaseSimpleLogin(ref, function(error, user){
if(error){
console.log(error);
}
else if(user){
console.log(user);
}
else{
console.log("user logged out");
}
});
// This shows a valid object
console.log($scope.auth);
$scope.createAccount = function(){
console.log("found me");
$scope.auth.$createUser($scope.email, $scope.password, function(error, user){
console.log("something");
console.log(user);
if(!error){
console.log(user);
}
else{
console.log(error);
}
});
};
});
When I bind the $scope.createAccount function to an ng-click event and click on the bound button, console.log("found me") runs in the browser, but none of the other console.log commands in $scope.createAccount is shown.
The console.log($scope.auth) command I have before setting the $scope.createAccount function shows a valid object with the $createUser function defined.
I am not receiving any console errors when I run $scope.createAccount so I am assuming the call has been "successfully" made.
Why am I able to see the auth object, but not receiving a callback after calling $createUser?
This was happening because I was doing callbacks based on JavaScript notation instead of Angular notation. Since I was using the AngularFire methods (they have the same names as the vanilla JavaScript SDK methods, but with a $ sign in front of them), I needed to handle callbacks using Angular's $promise methodology.
I changed
$scope.auth.$createUser($scope.email, $scope.password, function(error, user){
// do things;
});
to
$scope.auth.$createUser($scope.email, $scope.password)
.then(function(user){
// do things if success
}, function(error){
// do things if failure
});
and the callback worked as expected.
There is an exception to the above with the vanilla JS firebaseSimpleLogin constructor vs Angular $firebaseSimpleLogin constructor. On the vanilla JS constructor, there are callbacks on the constructor that allows you to specify what your script should do when a user logs in / logs out. It follows the following format:
var auth = new firebaseSimpleLogin(ref, function(error, user){
if(error){
// do things if login failure
}
else if(user){
// do things when login succeeds
}
else{
// do things when user logs out
}
});
If you try to do the same with the Angular constructor like so:
$scope.auth = $firebaseSimpleLogin(ref)
.then(function(user){
// do things when login succeeds
}, function(error){
// do things when login fails
});
you'll receive an error. There are no callback methods with the Angular constructor. I am guessing this was done on purpose since with Angular, you have data binding and you can simply $watch $scope.auth.user for changes and perform operations in your app depending on the variable's state. When $scope.auth.user is set to null, the user is logged out. If the value is set to anything else than null, then the user is logged in.