Server method not returning data to client - meteor

Here's the method located in server\twitter\methods.js
Meteor.startup(function(){
Meteor.methods({
searchTweets:function(term){
T.get('search/tweets', { q: term, count: 2 }, function(err, data) {
console.log(data);
return data;
})
}
});
});
Here's the event code located in client\search.js
Template.DashboardSearch.events({
"click #btnSearchTweets": function(event, template){
event.preventDefault();
var term = $('#searchTerm').val();
Meteor.call('searchTweets', term, function(err, result) {
console.log(err);
console.log(result);
if (err) {
console.log(err);
}
if (result) {
Session.set('tweetData', result);
}
});
$('#searchTerm').val("");
}
});
The thing is, the 2 console.log() statements in the search.js file both return undefined while the console.log() in methods.js returns the expected twitter api data, any clues?

I might've mislead some people with how I framed my question.
The thing is; I was using the meteorhacks:npm package & the T represent a Meteor.npmRequire() package.
This is how my method looks like now:
Meteor.methods({
searchTweets:function(term){
var tweets = Async.runSync(function(done) {
T.get('search/tweets', { q: term, count: 2 }, function(err, data) {
done(null, data);
});
});
return tweets.result;
}
});
Everything seems to be working correctly now.

Related

Using Jasmine, how can I test the returned value of asynchronized call?

I am using jasmine to test the features of redis. As the redis APIs are all asynchronized call, I don't know how to test the result with jasmine expect().toBe(). I always see the error:
throw err;
^
TypeError: Cannot call method 'expect' of null
Here is my test code:
var redis = require('redis');
describe("A suite for redis", function() {
var db = null;
beforeEach(function() {
db = redis.createClient();
// if you'd like to select database 3, instead of 0 (default), call
// db.select(3, function() { /* ... */ });
db.on("error", function (err) {
console.log("Error " + err);
});
});
afterEach(function() {
db.quit();
});
it('test string', function(){
db.set('str_key1', 'hello', redis.print);
db.get('str_key1', function(err,ret){
expect(ret).toBe('hello');
});
});
});
For synchronized call, may use Jasmine asynchronous feature, passing a done() to beforeEach() and it(), see:
http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
So, your code can be changed to:
var redis = require('redis');
describe("A suite for redis", function() {
var db = null;
beforeEach(function(done) {
db = redis.createClient();
// if you'd like to select database 3, instead of 0 (default), call
// db.select(3, function() { /* ... */ });
db.on("error", function (err) {
console.log("Error " + err);
});
done();
});
afterEach(function(done) {
db.quit();
done();
});
it('test string', function(done){
db.set('str_key1', 'hello', redis.print);
db.get('str_key1', function(err,ret){
expect(ret).toBe('hello');
done(); // put done() after expect(), or else expect() may report error
});
});
});
expect(val).toBe('hello');
I don't see "val" is defined in above code, you may want to check "ret".
expect(ret).toBe('hello');

Multiple Data Contexts in router - Meteor

I'm building a meteor app and on one route I'm adding multiple data context like so -
this.route('orgPage', {
path: '/org/:title',
data: {
orgs: function () {Orgs.findOne(this.params._id)},
projects: function() {Meteor.subscribe('projects', this.params._id)}
}
The only problem is that when I try to access this data in my templates js file, I can't access the _id or any of the attributes of orgs.
I've tried several approaches, but it always returns undefined. If I use a single data context, it works perfectly. Here is the function that doesn't function properly -
Template.orgPage.events({
'click #newProject': function(e) {
$('#npModal').modal();
},
'submit #npModal form': function(e, template) {
e.preventDefault();
if(!$(e.target).find('[name=newTitle]').val()) {
var projectTitle = 'Untitled'
} else {
var projectTitle = $(e.target).find('[name=newTitle]').val()
}
var theid = this._id;
var newProject = {
title: projectTitle,
organization: theid
}
Meteor.call('project', newProject, function(error, id) {
if (error)
return alert(error.reason);
$('#npModal').modal('hide');
$('#npModal').on('hidden.bs.modal', function (e) {
Router.go('newFields', {});
})
});
});
Anyone have any ideas? Thanks!!
You have missed a return statement. function () {Orgs.findOne(this.params._id)} should be function () {return Orgs.findOne(this.params._id)}. Further more, this inside this function won't refer to what you want, so you can't use this.params. And why do you subscribe to a subscription as a data context property? Do it in the waitOn function instead.

Meteor JS Iron Router subscribe to data

I am using Meteor 0.8 and iron-router.
I have a single route that I use to subscribe to a number of collections. What I would like to do is create a document when you arrive on the route and then subscribe to it so I can access it.
This is my route:
this.route('product_collection', {
path: '/collection/:collection',
template: 'product_collection',
layoutTemplate: 'layout-new',
waitOn: function() { return [ Meteor.subscribe('specific_product_collection', this.params.collection), Meteor.subscribe('product_types')] },
data: function () { return ProductCollections.findOne({ url: this.params.collection }) },
onBeforeAction: function(pause){
//Prevent double rendering
if (this.ready()) {
var productType = ProductTypes.findOne({url: this.params.collection })
console.log(productType)
if(productType){
var product = {
productType: productType._id
};
Session.set("productTypeId", productType._id);
Products.insert(product, function(error, id) {
if (error) {
console.error(error);
set_notification('Oops, something went wrong.', 'Please try again later.');
} else {
Session.set("productId", id);
Session.set("productName", productType.name );
//Not working
Meteor.subscribe('specific-product', id);
console.log('should be subscribed')
}
});
}
} else {
console.log('loading')
this.render('loading');
pause();
}
},
});
Ok,
I found out the ID was different on the server vs client side location. So ended up writing a meteor method call on the server.
See reference: Retrieve _id after insert in a Meteor.method call

I'm using Meteor, what do I need to do to wait for a promise to be returned from an API call?

if (Meteor.isClient) {
Template.hello.events({
'click input': function () {
//create a new customer
Meteor.call('createCustomer', function (error, result) {
console.log("Error: " + error + " Result: " + result); } );
}
});
}
if (Meteor.isServer) {
Meteor.methods({
createCustomer: function () {
try {
balanced.configure('MyBalancedPaymentsTestKey');
var customer = Meteor._wrapAsync(balanced.marketplace.customers.create());
var callCustomer = customer();
var returnThis = console.log(JSON.stringify(callCustomer, false, 4));
return returnThis;
} catch (e) {
console.log(e);
var caughtFault = JSON.stringify(e, false, 4);
}
return caughtFault;
}
});
}
And I just used the default hello world without the greetings line.
<head>
<title>testCase</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
<input type="button" value="Click" />
</template>
On the client side the log prints
Error: undefined Result: {}
On the server side the log prints
[TypeError: Object [object Promise] has no method 'apply']
Any idea how I can wait for that promise instead of returning the blank result?
I'm assuming balanced.marketplace.customers.create returns a Promises/A+ promise. This is an object with a method .then(fulfillmentCallback, rejectionCallback) - the fulfillmentCallback is called when the operation succeeds, and the rejectionCallback is called if the operation had an error. Here's how you could use Futures to synchronously get the value out of a promise:
var Future = Npm.require("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut["return"](result);
}, function (error) {
fut["throw"](error);
});
return fut.wait();
}
Then you can just call balanced.marketplace.customers.create normally (no _wrapAsync) to get a promise, then call extractFromPromise on that promise to get the actual result value. If there's an error, then extractFromPromise will throw an exception.
By the way, code in if (Meteor.isServer) blocks is still sent to the client (even if the client doesn't run it), so you don't want to put your API key in there. You can put code in the server directory, and then Meteor won't send it to the client at all.
Update this line
var customer = Meteor._wrapAsync(balanced.marketplace.customer.create)();
Another approach is to use Futures. I use this a lot on the server side to wait for results to return back to the client.
Here's a small example of that I use for logins:
Accounts.login(function (req, user) {
var Future = Npm.require("fibers/future");
var fut = new Future();
HTTP.call("POST", globals.server + 'api/meteor-approvals/token',
{
timeout: 10000, followRedirects: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
username: userName,
password: req.password
}},
function (err, result) {
if (err) {
logger.error("Login error: " + err);
fut.throw(err);
}
else {
fut.return("Success");
}
}
);
return fut.wait();
}

Cannot call method 'create' of undefined

Here is what I'm getting from the console server side.
I20140516-21:27:12.142(0)? There was an error on this page. Cannot call method 'create' of undefined
I am not finding a good reason why this method isn't defined. I have the balanced-payments-production package from Atmosphere loaded and this includes the balanced.js file and the api export to the server. Any help here is appreciated.
Here is my events.js file
Template.CheckFormSubmit.events({
'submit form': function (e, tmpl) {
e.preventDefault();
var recurringStatus = $(e.target).find('[name=is_recurring]').is(':checked');
var checkForm = {
name: $(e.target).find('[name=name]').val(),
account_number: $(e.target).find('[name=account_number]').val(),
routing_number: $(e.target).find('[name=routing_number]').val(),
recurring: { is_recurring: recurringStatus },
created_at: new Date
}
checkForm._id = Donations.insert(checkForm);
Meteor.call("addCustomer", checkForm, function(error, result) {
console.log(error);
console.log(result);
// Successful tokenization
if(result.status_code === 201 && result.href) {
// Send to your backend
jQuery.post(responseTarget, {
uri: result.href
}, function(r) {
// Check your backend result
if(r.status === 201) {
// Your successful logic here from backend
} else {
// Your failure logic here from backend
}
});
} else {
// Failed to tokenize, your error logic here
}
// Debuging, just displays the tokenization result in a pretty div
$('#response .panel-body pre').html(JSON.stringify(result, false, 4));
$('#response').slideDown(300);
});
var form = tmpl.find('form');
//form.reset();
//Will need to add route to receipt page here.
//Something like this maybe - Router.go('receiptPage', checkForm);
},
'click [name=is_recurring]': function (e, tmpl) {
var id = this._id;
console.log($id);
var isRecuring = tmpl.find('input').checked;
Donations.update({_id: id}, {
$set: { 'recurring.is_recurring': true }
});
}
});
Here is my Methods.js file
function getCustomer(req, callback) {
try {
balanced.marketplace.customers.create(req, callback);
console.log(req.links.customers.bank_accounts);
}
catch (error){
var error = "There was an error on this page. " + error.message;
console.log(error);
}
}
var wrappedGetCustomer = Meteor._wrapAsync(getCustomer);
Meteor.methods({
addCustomer: function(formData) {
try {
console.log(formData);
return wrappedGetCustomer(formData);
}
catch (error) {
var error = "There was an error on this page." + error.message;
console.log(error);
}
}
});
I needed to run balanced.configure('APIKEYHERE'); first, then run the balanced code.

Resources