How to send multiple responses to one request in Meteor - meteor

I have a piece of code where I use a 3rd party api to get data from, lets say Facebook,
I do the following query
Meteor.call('getAwesomeUsers', function() { ... });
now on the server this is I have
_.each(['zuck','shane', 'dustin'], function(key, value) {
console.log(key, value)
var data= HTTP.get( 'https://graph.facebook.com/' ).data;
console.log(data);
return data;
});
(Please ignore the code, just get the idea of returning the data per user request,)
So as the code shows there are three requests made to the server, I can see the data is grabbed properly using my console.log() but the data is not returned to the client.
All I want to know is how do I send this data down to the client in seperate three times.
UPDATE
I just make 3 requests to the server at the moment just to make things work

You can't. This isn't even a question of meteor or javascript, it's a matter of programming paradigm. A function returns only once, not multiple times.
So you have two options:
bundle the responses into one
create a separate communication that you can use, applying your own logic about when to send something and how to react to receiving data on the other end.
For 1. you can simple do this:
return _.map(['zuck','shane', 'dustin'], function(key, value) {
console.log(key, value)
var data= HTTP.get( 'https://graph.facebook.com/' ).data;
console.log(data);
return data;
});
which will just give you an array with three "responses".

Related

Meteor Dashboard and publication/subscription

EDIT: as the original question was too vague I have updated it to make it more concrete
I'd like to create a dashboard in Meteor that shows some statistics about my collections (e.g. how many docs, how many users...). I have been trying the past days but can't seem to find a good/intelligent way.
I initially just did the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Products.find().count();
}
});
This did not work. I think because it counts the number of products from minimongo, but not sure.
Then I did the following:
In the template helper, call a method and get the value to show on the dashboard page (does not work)
Was told not to use pub/sub mechanism for this type of metric
Worked via Session variables (did work, but feels a bit strange to store this kind of metric data in Session variables
So then I read in another SO response about Reactive Variables and tried the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Template.instance().myAsyncValue.get();
}
});
Template.dashboard.created = function() {
var self = this;
self.myAsyncValue = new ReactiveVar("Waiting for response from server");
Meteor.call('getProductsCount', function(error, asyncValue){
if (error)
console.log(error);
else
self.myAsyncValue.set(asyncValue);
});
};
This works, but I find this extremely difficult for something as simple as showing a product count (or any other metric). Not sure I understand the reason why I should use sth as reactive variables?
Then -out of curiosity- I tried the following (using meteor add simple:reactive-method) and it works:
Template.customerDashboard.helpers({
getProductsCount: function () {
return ReactiveMethod.call("getProductsCount");
}
});
So the question really is why having to use Reactive variables and methods for sth as simple as this. Can someone explain?
If you want to show the count only in the view, the best way is to return the count number only. you do not need publish/subscribe at all. you can use server methods. and if you want to show data also, you can go for pub-sub. and your approach is correct.

Why call collection.find in an Iron Router controller

I have built a small meteor app based on code generated by the excellent Meteor Kitchen project. This code works and renders the collection to the page, but there is one thing I am confused about.
A subset of the code is here:
router.js
this.route("articles", {path: "/articles", controller: "ArticlesController"});
ArticlesController
this.ArticlesController = RouteController.extend({
template: "Articles",
onBeforeAction: function() {
this.next();
},
action: function(){
if (this.isReady()) {
this.render();
} else {
this.render("loading");
}
},
isReady: function() {
var ready = true;
var subs = [ Meteor.subscribe('allArticles') ];
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
articles: Articles.find({})
};
}
});
server/pubs/articles.js
Meteor.publish('allArticles', function() {
return Articles.find({});
});
Meteor.publish('singleArticle', function(articleId) {
check(articleId, String);
return Articles.find({_id: articleId});
});
As I understand how this code is working, the following takes place:
(1) Collection is published via allArticles and singleArticle subscriptions
(2) ArticlesController subscribes to allArticles
(3) data function in the ArticlesController extracts the data (from the subscription?) to the articles array which is then exposed to the Blaze template.
Where I am confused:
Why do we need to do a Articles.find({}) in the data function? Where do we access the allArticles data ... it seems we are going back to the Articles collection directly and how is that possible if we have subscribed only to allArticles ?
While you don't show it, I'm assuming you have the following line in your code, defined somewhere that will execute it on both the server, and the client:
Articles = new Mongo.Collection('articles');
/*CollectionName = new Mongo.Collection('DBCollectionName');*/
Docs. When this is executed on the server a Collection is created, and assigned to the variable name Articles. 'articles' is the name used to store this collection in MongoDB.
When this is executed on the client, a mini mongo Collection is created. It initially will have no documents in it.
Next you have server only code:
Meteor.publish('allArticles', function() {
return Articles.find({});
});
Meteor.publish('singleArticle', function(articleId) {
check(articleId, String);
return Articles.find({_id: articleId});
});
Docs. This defines two publications, 'allArticles' and 'singleArticle'. These are not Collections themselves, but are rules that specify a set of data that the server will publish, and a client may subscribe to. While these two publications return data from the Server's Articles collection, publications can return data from one or more collections, or by directly using the underlying ddp protocol you can publish data that comes from another data source (not mongodb).
Next on the client you subscribe to a collection:
Meteor.subscribe('allArticles')
Docs. This call takes the name of a publication defined on the server ('allArticles'), and subscribes to it. The server then executes the publish function, and sends over ddp the set of data returned. This data is stored in the Client-side Mini Mongo Collection created above, and named Articles.
Also the server will monitor the Articles collection for changes, and if the resultset of the 'allArticles' publication changes, will send these changes as updates to the client.
So next you have the data function in your Controller (Client side).
data: function() {
return {
articles: Articles.find({})
};
}
Docs. This sets the data context for the render function.
The reason this calls Articles.find rather than allArticles.find is because allArticles is not a collection, but was instead the name of the publication the client used to request the server send data, that was stored in the clients mini mongo collection named Articles.
Where do we access the allArticles data ... it seems we are going back
to the Articles collection directly and how is that possible if we
have subscribed only to allArticles ?
You return this as part of your data object so that you can access it in your template. In your Articles template you can now use {{#each articles}} directly without a helper function because articles is part of your data context. You can also access the articles returned from your controllers data portion inside of your Articles template helpers by using this.articles.
Why do we need to do a Articles.find({}) in the data function?
These queries being performed in your controllers data function act on the clients minimongo Articles collection as opposed to the servers. Once the information is published from the server, and the client has subscribed to it, the client has this information available in their minimongo instance, but still needs to access it somehow. Basically, the publication makes the information available, but the Articles.find({}) accesses it for the client.
Accessing this information inside of the data function of your controller is simply to avoid doing it inside of your template.
I think that your misunderstanding comes from the third step that you have described:
data function in the ArticlesController extracts the data (from the
subscription?) to the articles array which is then exposed to the
Blaze template.
The data function extracts the data from minimongo on the client which contains the information from the subscription. Minimongo lies between the subscription and the data function.
I would need to know more about your app to answer the question. Are you viewing just a single article, or is there a page that lists them all?
If you are viewing a single article you would need to subscribe to the singleArticle publication.
If you were showing a list of articles, you would need to subscribe to allArticles. If there are a lot of articles, you could improve the speed of your app by limiting the number of fields with a query projection.

Wait for latest values from dependent ajax streams in BaconJS?

I have 3 streams. (1) gradingResult and contextId are ajax requests that depend on studentResponse. (2) For each studentResponse value, I need to fire an event with the studentResponse value and the corresponding result from the other two streams.
This question is similar but different than Wait for latest values from dependent streams in BaconJS?. One key difference is that I don't know which of gradingResult and contextId will return first.
A note about the code: the code is based on production code. I do not have access to gradingResult or contextId. All I know is that they're ajax requests that take inputs from studentResponse.
Some sample code with desired output:
function _fauxAjax(val) {
return Bacon.fromBinder(function(sink) {
setTimeout(function() {
sink(val);
sink(new Bacon.End());
}, Math.random()*1000);
return function() {}
});
}
var studentResponse = new Bacon.Bus();
var gradingResult = studentResponse.flatMap(_fauxAjax);
var contextId = studentResponse.flatMap(_fauxAjax);
Given this input:
studentResponse.push(1);
studentResponse.push(2);
studentResponse.push(3);
Desired output:
{studentResponse:1, gradingResult:1, contextId:1 }
{studentResponse:2, gradingResult:2, contextId:2 }
{studentResponse:3, gradingResult:3, contextId:3 }
If you define the two latter ajax requests as independent streams, I don't see a practical solution for making sure the values are related.
So, to make absolutely sure that you get the gradingResult and contextId responses that were triggered by the same studentResponse I'd say you have to do something like this:
studentResponse.flatMap(function(response) {
return Bacon.combineTemplate({
studentResponse: response,
gradingResult: _fauxAjax(response),
contextId: _fauxAjax(response)
})
}).log()

Angularjs multiple $http.get request

I need to do two $http.get call and I need to send returned response data to my service for doing further calculation.
I want to do something like below:
function productCalculationCtrl($scope, $http, MyService){
$scope.calculate = function(query){
$http.get('FIRSTRESTURL', {cache: false}).success(function(data){
$scope.product_list_1 = data;
});
$http.get('SECONDRESTURL', {'cache': false}).success(function(data){
$scope.product_list_2 = data;
});
$scope.results = MyService.doCalculation($scope.product_list_1, $scope.product_list_2);
}
}
In my markup I am calling it like
<button class="btn" ng-click="calculate(query)">Calculate</button>
As $http.get is asynchronous, I am not getting the data when passing in doCalculation method.
Any idea how can I implement multiple $http.get request and work like above implementation to pass both the response data into service?
What you need is $q.all.
Add $q to controller's dependencies, then try:
$scope.product_list_1 = $http.get('FIRSTRESTURL', {cache: false});
$scope.product_list_2 = $http.get('SECONDRESTURL', {'cache': false});
$q.all([$scope.product_list_1, $scope.product_list_2]).then(function(values) {
$scope.results = MyService.doCalculation(values[0], values[1]);
});
There's a simple and hacky way: Call the calculation in both callbacks. The first invocation (whichever comes first) sees incomplete data. It should do nothing but quickly exit. The second invocation sees both product lists and does the job.
I had a similar problem recently so I'm going to post my answer also:
In your case you only have two calculations and it seems to be the case this number is not mutable.
But hey, this could be any case with two or more requests being triggered at once.
So, considering two or more cases, this is how I would implement:
var requests = [];
requests.push($http.get('FIRSTRESTURL', {'cache': false}));
requests.push($http.get('SECONDRESTURL', {'cache': false}));
$q.all(requests).then(function (responses) {
var values = [];
for (var x in responses) {
responses[x].success(function(data){
values.push(data);
});
}
$scope.results = MyService.doCalculation(values);
});
Which, in this case, would force doCalculation to accept an array instead.

Meteor ad hoc record sets

I am writing an app that involves creating multiple subsets of the same collection, and publishing them under different record sets following this example.
Using this principle, I am creating ad hoc record sets. The publish code is in a method that gets called per template:
//Template
Template.item._item = function() {
Meteor.call('publishMethod', foo);
Meteor.subscribe('name-'+foo);
return someFunction(foo);
}
//Method
Meteor.methods({
'publishMethod' = function(foo) {
Meteor.publish('name-'+foo, function() { someFunction(foo); });
});
});
//Common area
someFunction = function(foo) {
return Collection.find({'foobar' : foo});
}
In this example, someFunction() sits in common area between client and server. someFunction() returns a subset of a collection based on foo.
I have some questions regarding the above approach:
When a method is called with the same foo value, Meteor prints "Ignoring duplicate publish named 'name-foo'". Is there any way to check if a record set exists?
There is concern that these record sets will continue to be published and not release memory. Are these record sets client side only? Or will they accumulate on the server?
This is the best approach I have found for dealing with multiple, complex queries on the same large dataset, and allows for specific fields to be sent per request and page. I am however open to suggestions.
Thanks in advance.

Resources