Correct usage of Meteor wrapAsync - meteor

The following is what I am trying to do:
var joinNetwork = function (obj) {
Meteor.call("joinNetwork", {
userId: obj.userId,
domain: obj.domain
}, function (err, networkId) {
return networkId;
});
}
Accounts.onCreateUser(function (options, user) {
var userId = user._id;
var email = options.email;
var domain = Utils.getDomain(email);
var joinNetworkSync = Meteor.wrapAsync(joinNetwork);
// works fine until here
var networkId = joinNetworkSync({
userId: userId,
domain: domain
});
// never get here
debugger
As you can see, after I call joinNetworkSync I never reach the code after it. In other words, networkId is never available. What am I doing wrong?

To return from a wrapAsync you have to call a callback passed to that function:
Meteor.wrapAsync(function (obj, done) {
Meteor.call("joinNetwork", {
userId: obj.userId,
domain: obj.domain
}, function (err, networkId) {
done(networkId);
});
})
You don't need wrap async here dough. When you call meteor methods server side, they return like normal functions. You can just do this if the joinNetwork method is properly defined:
Accounts.onCreateUser(function (options, user) {
var userId = user._id;
var email = options.email;
var domain = Utils.getDomain(email);
var networkId = Meteor.call("joinNetwork", {
userId: obj.userId,
domain: obj.domain
});
...
})

I think your sync version of joinNetwork is not returning anything. You placed a return inside another function, the callback of joinNetwork. Try splitting the next part up in another function and call that inside the callback function using the networkId.

Related

Meteor reactive publish

This Meteor code displays a message on a headerLabel on a template, the server and/or the client changes the message by inserting a new message in HeaderLabelCol mongo collection and expect the client template to change since it publishes the last inserted document.
I was able to insert a new message using the client browser but did not show till I refreshed the page which may indicate that the reactiveness chain is broken somewhere. What is the problem? How can it be fixed? Thanks
//client.js
Template.header.helpers({
headerLabel: function () {
return HeaderLabelCol.findOne() ? HeaderLabelCol.findOne().headerLabel : 'Make a selection';
}
});
//server.js
HeaderLabelCol = new Mongo.Collection('headerLabelCol');
Meteor.publish('headerLabelCol', function () {
return HeaderLabelCol.find({userId: this.userId}, { sort: { createdAt: -1 } });
});
HeaderLabelCol._ensureIndex({createdAt: -1});
HeaderLabelCol.before.insert(function (userId, doc) {
doc.userId = userId;
doc.createdAt = Date.now();
});
HeaderLabelCol.allow({
insert: function (userId, doc) {
return (userId && doc.owner === userId);
}
});
I think you need to add the condition in your helper as well.
//client.js
Template.header.helpers({
headerLabel: function () {
var result = HeaderLabelCol.findOne({}, { sort: { createdAt: -1 } });
return result ? result.headerLabel : 'Make a selection';
}
});

adding a field to a user after created

This code attempts to add a field to a user which already exist in meteor users.
The error I am getting is
Exception while invoking method 'logMeIn' Error: insert requires an argument
Which I don't understand, how can it be fixed? Thanks
/////////////////////////////////////
// client code
/////////////////////////////////////
Template.login.events({
'click #logMe': function() {
var username = $('#id').val();
var password = $('#pin').val();
Meteor.call('logMeIn', [username,password], function (err, data) { //create new user
if ( err ) {
if (err.message.match(/username already exists/i)) {
Meteor.loginWithPassword(username+password,password)
}
} else {
console.log('new user created');
}
});
}
});
/////////////////////////////////////
// server code
/////////////////////////////////////
Meteor.methods({
logMeIn: function (credentials) {
//do work , if logged in, do next line
var idPin = credentials[0] + credentials[1];
Accounts.createUser({username: idPin, password: credentials[1]});
}
});
Accounts.onCreateUser(function (options, user) {
user.menuGroup = 'a';
});
You need to return the user on the Account.onCreatedUser (documentation here). Also, additional data of the user should be put under the profile branch (check the documentation in here)
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
if (user['profile'] == null) {
user['profile'] = {};
}
user['profile']['menuGroup'] = 'a';
return user;
});

Returning undefined object from a firebase snapshot

I implemented a function where I want to return an object saved under a certain url. In the code below, the first 'console.log(result);' returns the right object from the firebase location. The second one return undefined. Can somebody explain why and how to fix it?
_getById: function(obj) {
var url = "https://my-app.firebaseio.com/list/" + obj.groupId;
console.log(url);
var ref = new Firebase(url);
var result = {};
ref.on("value", function(snapshot) {
result = snapshot.val(); //first
console.log(result);
}, function (errorObject) {
}
);
console.log(result); //second
return result;
},
The data is loaded from Firebase asynchronously. So you'll notice that your second console.log() displays before the first one. You cannot return data that is being loaded asynchronously.
You'll have to change the way you code. Instead of "get the id, then do something with it", you need to "do something whenever the id is loaded/changed".
So instead of:
var list = _getById({ groupId: 42});
console.log("Our list is: "+list);
You'll:
_getById({ groupId: 42 }, function(list) {
console.log("Our list is: "+list);
});
_getById: function(obj, callback) {
var url = "https://my-app.firebaseio.com/list/" + obj.groupId;
console.log(url);
var ref = new Firebase(url);
var result = {};
ref.on("value", function(snapshot) {
result = snapshot.val(); //first
callback(result);
}, function (errorObject) {
});
console.log(result); //second
return result;
},
In the above code we're passing a callback into _getById() and invoke that callback when the list has loaded (and whenever the list changes).
Some further reading material:
Polymer Firebase: Print async data
Asynchronous access to an array in Firebase
Trying to get child records from Firebase
Handling Asynchronous Calls (Firebase) in functions

Meteor showing Accounts.createUser errors

I have the following code in my Meteor app where I create new users, assign them 'basic' role. Yet I am having a trouble showing on the client side errors returned while processing Accounts.createUser, can someone please tell me how I can return errors returned by Accounts.createUser while having it on the server as my code below. Thanks
/server/users.js
Meteor.methods({
'createMemberAccount': function (data, role) {
var userId;
Meteor.call('createNewAccount', data, function(err, result) {
if (err) {
return err;
}
console.log('New account id: '+ result);
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createNewAccount': function (adminData) {
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
}
});
/client/signup.js
Template.signupForm.events({
'submit #signup-form': function(e, t){
e.preventDefault();
var userData = {};
userData.email = $(e.target).find('[name=email]').val();
userData.password = $(e.target).find('[name=password]').val();
userData.roles = ['basic'];
Meteor.call('createMemberAccount', userData, 'basic', function(err, userId) {
if (!err) {
console.log('All OK');
} else {
console.log('Error: ' + err.message);
}
});
return false;
}
});
Since You are creating an static rol "basic", you don't need to do that pair of methods, and Meteor.calls, instead you can use
So, use the v on the client side, just like this.
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.createUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
}
});
return false;
}
});
If you see there is not any role yet incude, so now on the server.js use the onCreateUser method.
//Server.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.role = "basic"
return user;
});
Now thats is more easy, and with less code, if you are trying to create 2 differents roles like "Admin" and "Basic", just on the client side create a profile field named "profile.roles" and do a if statement on the onCreateUser.
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
This part returns the userId once it is created, it doesn't return any errors when it fails.
When it fails, the returned value will be undefined
Also, in the server, we cannot use callbacks with Accounts.createUser
If you want find the errors, you have to use Accounts.createUser in client side.
Coming to this late, but on the server side, you can assign the createUser to a variable and it will return the new user’s _id; then you can check if that exists. For example (server side only):
let email = 'foo#bar.com';
let password = 'bar';
let profile = {firstName: 'foo', lastName: 'bar'};
let newId = Accounts.createUser({
password: password,
email: email,
profile: profile
});
if (!newId) {
// New _id did not get created, reason is likely EMail Already Exists
throw new Meteor.Error(403, "Cannot create user: " + error.reason);
}
else {
// Stuff here to do after creating the user
}
The Meteor.Error line will be passed back as an error in the callback on the client side, so you can reflect that error to the browser.

using Future.wait() in Meteor.methods

Running subscriptions in async mode with new Future() works fine but if the same thing is done in a Meteor.method the application crashes with a message that it can not wait without a fiber. But I have to return something from the Meteor.method
This is the method:
/**global variables*/
var Fiber = Npm.require("fibers");
var Future = Npm.require("fibers/future");
Meteor.methods({
'single-data': function (form, state, tenant, selectedRow, search, sort) {
var user = Meteor.users.findOne({_id: this.userId});
/**check if the user can see the data in the state*/
if (!(isAdmin(user, form) || hasPermission(user, form, state, "read"))) {
return null;
}
/** set the sort order for the query*/
var order = [];
if (sort) {
order.push(["fieldData." + sort.column + ".0.value", sort.order == 1 ? "asc" : "desc"]);
}
order.push(["submitTime", "desc"]);
var query = dataQuery(form, state, _.uniq(_.pluck(user.groups, "tenant")), search);
var fut = new Future();
var fut = new Future(); setTimeout(function () {
fut.ret(FormDatas.find(query, {sort: order, skip: selectedRow, limit: 1}).fetch()[0]);
}, 0 * 1000);
// Wait for async to finish before returning the result
return fut.wait();
}
});
I believe that the return value from future should not contain asynchronous code, it should happen before calling ret(). Anyway, in your case future is not needed, simply call
return FormDatas.findOne(query, options);

Resources