My angular controller looks like this:
angular.module("campos").controller("HomeCtrl", ['$scope', '$meteor', '$rootScope', '$state', '$modal',
function ($scope, $meteor, $rootScope, $state, $modal) {
// $scope.users = $meteor.collection(Meteor.users, false).subscribe('users');
$meteor.autorun($scope, function () {
$scope.$meteorSubscribe('myOrgnization', 'crown').then(function () {
$scope.organization.forEach(function (org) {
console.log(org._id);
});
},
function (error) {
console.log(error);
});
});
$scope.organization = $meteor.collection(function(){
return Organizations.find({});
});
}
]);
The server code looks like :
Meteor.publish('myOrgnization', function(org){
return Organizations.find({
'code' : org
});
});
The client controller finished properly, from the console I can get the the record id properly. However, I got an exception thrown out:
TypeError: undefined is not a function
at http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:390:12
at Array.forEach (native)
at Function._.each._.forEach (http://localhost:8026/packages/underscore.js?46eaedbdeb6e71c82af1b16f51c7da4127d6f285:149:11)
at diffArray (http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:388:5)
at updateCollection (http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:1310:3)
at http://localhost:8026/packages/urigo_angular.js?de756130fe1ce5aaa41d3967997c1b2090bcdd1b:1154:11
at http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:17819:31
at completeOutstandingRequest (http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:5529:10)
at http://localhost:8026/packages/angular_angular.js?feeaf4750c9fd3ceafa9f712f99a21ee6fef9b41:5806:7
home.ng.js:8 dPmZoCeJ9YbKxKA4u
It seems coming from angular defer, but I think I am following the subscribe syntax properly ? even I change it to use $meter.subscribe, it throws the same exception.
Any help will be much appreciated.
Thanks in advance.
You should assign the $scope collection after the promise returns:
$scope.$meteorSubscribe('myOrgnization', 'crown').then(function () {
$scope.organization = $scope.$meteorCollection(function(){
return Organizations.find({});
});
});
I would also suggest passing an object for your publish arguments:
$scope.$meteorSubscribe('myOrginization', {arg1: 'crown'}).then(...)
Then of course on the server you would get:
Meteor.publish('myOrgnization', function(org){
return Organizations.find({
'code' : org.arg1
});
});
Related
I am trying to get data from AirVisual API using meteor methods on the server and passing it to the client. The data is successfully received on the server. However, the template helper gets undefined when the method is called in it.
Client Helper:
Template.index.helpers({
getCityDataOnClient: function(city, state) {
Meteor.call('getCityData', city.toLowerCase(), state.toLowerCase(), function(error, result) {
if(!error) {
console.log(result); //returns undefined
}
else {
console.log(error);
}
});
}
});
Meteor methods.js in lib folder:
Meteor.methods({
getCityData : function(city, state) {
var data = [];
const result = HTTP.call('GET', 'http://api.airvisual.com/v2/city', {
params: {
state: state,
city : city,
country: 'pakistan',
key: 'xxxxxxxxxx'
}
}, function(err, res) {
if (!err) {
data = res.data.data;
//console.log(data); //prints correct data on the server and client
return data;
}
else {
console.log(err);
return err;
}
});
}
});
I have already looked up answers on similar questions. Nothing seems to work, including Tracker, reactive-var, and reactive-methods.
The problem here is that you are trying to return data from inside a callback to a function that 1. isn't waiting for you, and 2. has already returned.
Thankfully, Meteor does some magic on the server to make asynchronous calls like HTTP.call appear synchronous.
Your method can be done like so:
Meteor.methods({
getCityData : function(city, state) {
const result = HTTP.call('GET', 'http://api.airvisual.com/v2/city', {
params: {
state: state,
city : city,
country: 'pakistan',
key: 'xxxxxxxxxx'
}
});
return result.data.data;
}
});
By excluding the callback on Meteor's HTTP module, Meteor will run it in a Fiber and wait for the result before continuing execution (like with async/await)
If you were using a third party library for HTTP requests, you would need to wrap the function using Meteor.wrapAsync to get the benefit of running in a fiber. Or you could wrap it in a promise and return the promise from the method
I have a peice of code;
Router.configure({
layoutTemplate: 'master_layout'
});
Router.map(function(){
this.route('generatePDF', {
path: '/pdf',
where: 'server',
action: function() {
console.log(voters.findOne().fetch());
var voters = voters.find();
....
}
});
How can I use any given collection inside action of routes.
i get error as below;
W20160510-20:19:29.909(5.5)? (STDERR) TypeError: Cannot call method 'findOne' of undefined
W20160510-20:19:29.910(5.5)? (STDERR) at [object Object].action (lib/routes.js:40:29)
W20160510-20:19:29.910(5.5)? (STDERR) at boundNext (packages/iron_middleware-stack/lib/middleware_stack.js:251:1)
Try this:
action: function () {
this.render('someTemplate',{
data: function(){
return Voters.find()
}
});
}
You've got multiple errors in your code.
Firstly, voters is undefined in the console.log line (as your error log says).
Secondly, Voters.findOne() returns a single document (assuming you have a collection called Voters). You can't call fetch on a document.
You can call fetch on a cursor. For example, Voters.find().fetch() would be fine.
Hope that helps
I found this error in the server console:
Exception in defer callback: TypeError: undefined is not a function
at packages/ddp/livedata_server.js:1054:1
at Array.forEach (native)
at Function..each..forEach (packages/underscore/underscore.js:105:1)
at [object Object]._.extend._callStopCallbacks (packages/ddp/livedata_server.js:1053:1)
at [object Object]._.extend._deactivate (packages/ddp/livedata_server.js:1043:1)
at packages/ddp/livedata_server.js:803:1
at Function..each..forEach (packages/underscore/underscore.js:113:1)
at [object Object]._.extend._deactivateAllSubscriptions (packages/ddp/livedata_server.js:802:1)
at packages/ddp/livedata_server.js:444:1
at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
And here the code I use:
/* Source: http://phucnguyen.info/blog/how-to-publish-to-a-client-only-collection-in-meteor/ */
var subs = {};
Meteor.publish(
// THROW AN 'EXCEPTION IN DERFER CALLBACK: ...'
'helperPublication',
function () {
var subscription = this;
subs[subscription._session.id] = subscription;
Datasets.find().map(function (dataset) {
subscription.added(
'subdatasets',
dataset._id,
{
name: dataset.name,
data: [], // To avoid "Uncaught TypeError: Cannot read property 'length' of undefined" error on load
peaks: [] // Idem
}
)
});
subscription.onStop();
subscription.ready()
});
You can find the whole app in the following meteorpad: http://meteorpad.com/pad/6NDneym2qEW7pF9JM/ClearChrom
Ok, So with that info; I think the best way to go about doing this is to have separate collection for that data. Because then you can easily change how much data should be displayed. The publication for that collection could look like this:
Meteor.publish('data', function publishData (limit) {
return DataCollection.find({}, {
fields: {
name: 1,
data: 1,
peaks: 1
},
limit: limit
})
});
Note that the callback of the publication takes an argument limit. You can now subscribe to it like this:
Meteor.subscribe('data', 3)
And whenever you need more data you can just:
Meteor.subscribe('data', 6)
So this solution is reactive and very clean (in my opinion at least).
I also checked your existing script:
var subs = {};
Meteor.publish(
'helperPublication',
function (limit) {
var subscription = this;
subs[subscription._session.id] = subscription;
// I'd use forEach here, because you're not modifying the document
Datasets.find().forEach(function (doc) {
subscription.added(
'subdatasets',
doc._id,
{
name: doc.name,
// Whith the slice function you can get a subset of the data array
data: doc.data.slice(0, limit),
peaks: doc.peaks
}
)
});
// Calling `subscription.onStop` without an argument doesn't do anything
// Except maybe through an error
subscription.onStop(function () {
// clean the subscription from subs again
delete subs[subscription._session.id];
});
// This shouldn't be necessary.
// subscription.ready()
});
There are some issues with this still. For one thing I'd advise you to try avoiding meteor attributes with an underscore in front. They might be removed or changed in future releases.
The page still renders as it should, but this appears in my console log.
Exception in callback of async function: TypeError: Cannot read
property 'username' of undefined
at null. (http://localhost:3000/router.js?39194109cc7760190c519a386d66c807ba19230c:48:9)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at onRerun (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:520:9)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at onRun (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:505:11)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at dispatch (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:448:3)
Which points to the line indicated below:
var OnBeforeActions = {
loginRequired: function() {
if (!Meteor.userId()) {
this.render('hello');
} else {
this.next();
}
}
};
Router.onBeforeAction(OnBeforeActions.loginRequired, {
only: ['home']
});
Router.route('/', function() {
// this.render('homepage');
Router.go('/users/' + Meteor.userId());
});
Router.route('/users/:user_id', function() {
this.render('home', {
data: {
username: Meteor.users.findOne({
_id: this.params.user_id
}).username, // THIS LINE HERE
}
});
});
The idea is to redirect the user to his page if he is already logged in - and it appears to work as it should, but why am I getting this?
When the browser loads initially there will not be any data available (all the collections will be empty - until they are downloaded. Thus the result of the query Meteor.users.findOne({_id: this.params.user_id}) will be undefined.
This is the reason you can't read username out of undefined.
Since the query will re-run/it is reactive you just need to take account when it is null and it will sort itself out. user.username is only read if user is not null:
Router.route('/users/:user_id', function() {
var user = Meteor.users.findOne({_id :this.params.user_id});
this.render('home', {
data: {
username: user && user.username
}
});
});
I am trying to use Twitter REST API GET followers/ids and save it to Mongo using Mongo collection insert method
Code inside /server/server.js:
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
var getTwitterFollowersIDsAsync = function (screenname, cb) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
);
};
Meteor.methods({
getTwitterFollowersIDsCollectionsClient : function (screenname){
var getTwitterFollowersIDsNow = Meteor.wrapAsync(getTwitterFollowersIDsAsync);
var result = getTwitterFollowersIDsNow('meteorjs');
console.log(result);
return result;
}
});
});
Error in server console:
Error: Meteor code must always run within a Fiber.
Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
Objective is to save twitter followers to a Mongo collection.
Meteor v. 1.1.0.2
Meteor packages:
meteor-platform
autopublish
insecure
differential:vulcanize
accounts-twitter
accounts-ui
meteorhacks:npm
npm-container
npm modules being used inside Meteor through meteorhacks:npm: "twit": "1.1.20" (added inside packages.json)
**UPDATE Second attempt **
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (){
setTimeout(function(){
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
},10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
});
}
});
});
I'm then calling the below code from browser console:
Meteor.call('getTwitterFollowersIDsCollectionsClient');
The server crashes with the same error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
UDPATE:
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function(screenname){
T.get('followers/ids', { screen_name: screenname }, Meteor.bindEnvironment(function (err, data, response) {
console.log("from getTwitterFollowersIDsCollectionsClient : "+data.ids);
var vids = data.ids;
for(var i in vids)
{
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
return data;
}));
},10);
return;
}
Added Meteor.bindEnvironment to T.get callback method. This code worked and I was able to save the follower IDs to a mongo collection
Glad you got it working, but I played around with this and Meteor provides another, super easy way: wrapAsync. At least, it was easy once I figured it out! Here's the server code I wound up with -
var T = new TwitMaker({
consumer_key: '...'
, consumer_secret: '...'
, access_token: '...'
, access_token_secret: '...'
})
var wrapGet = Meteor.wrapAsync(T.get, T);
Meteor.methods({
getTwitImg: function(target) {
data = wrapGet('users/show', {screen_name: target});
if (data) {
img_url = data['profile_image_url'];
US.update({twitter: target}, {$set: {'targetImg': img_url}});
return img_url;
}
}
});
For the client and template code see this gist: https://gist.github.com/DanAncona/a09ce375e48bfa8efeca
Your code is a bit confusing. It seems like you're trying to execute a web service call async, but still return the result immediately (which won't work).
First of all, you probably wouldn't need to wrap the function to fetch the followers in an async block.
If you want your server method to return something immediately to the client after it has been called, I'd use a Meteor.setTimeout (see What's the point of Meteor.setTimeout() vs just setTimeout()?) block and call another method to do the fetching:
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function() {
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
}, 10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
}
});
Ideally you would use a template helper to retrieve your followers from your collection. Due to these kind of helpers being reactive, you could just call the server method from the client and let the reactivity of Meteor solve your problem of returning the followers via the helper (which is re-executed/re-rendering the template on data change).
try calling:
var wrappedInsert = Meteor.bindEnvironment(function(tweet) {
TweetsCollection.insert(tweet);},
"Failed to insert tweet into Posts collection.");
from inside of api callback
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
for(var i in data)
{
wrappedInsert(data[i]);
}
});
}