I am trying to get the value of a field from a document returned via a subscription. The subscription is placed inside a helper function. I had a callback function within the subscription return this value and then I assigned the return value to a variable (see code). Finally, I had the helper return this value. However, the value returned is a subscription object (?) and I can't seem to get anything out of it.
Code:
Template.myTemplate.helpers({
'returnUser':function(){
var id = Session.get('currentUserId');
var xyz = Meteor.subscribe('subscriptionName',id,function(){
var user = accounts.find().fetch({_id: id})[0].username;
return user;
}
return xyz;
}
});
Any help will be much appreciated. :)
You have to load first your subscriptions when the template is created, this creates an instance of your data with Minimongo.
Template.myTemplate.onCreated(function () {
var self = this;
self.autorun(function() {
self.subscribe('subscriptionName',id);
});
});
Then in the helper, you can make a query to retrieve your data
Template.myTemplate.helpers({
'returnUser': function(){
var id = Session.get('currentUserId');
return accounts.findOne(id).username;
}
});
Related
I'm stumped here. I can't get it to find a collection from the onCreated method. If I log the data.source_id right before the call and then do the same lookup in the console, it finds it. Is there something special about onCreated or something? Am I just doing it wrong?
client/setup.js
Meteor.subscribe('source_elements');
Meteor.subscribe('internal_elements');
client/submit.js
Router.route('/element/submit', function() {
this.render('submit', {
data: {
source_id: this.params.query.source_id,
},
});
});
Template.submit.onCreated(function() {
var data = Template.instance().data;
var source_element = SourceElements.findOne({'_id': data.source_id});
console.log(source_element); // EMPTY!!
});
Template.submit.helpers({
element: function() {
var data = Template.instance().data;
var source_element = SourceElements.findOne({'_id': data.source_id});
console.log(source_element); // RESULT!!
return source_element;
},
});
Subscriptions are asynchronous. It looks like you are creating the template before the data has arrived at the client. By the time you execute the find in the console, the client has received the data.
Inside your onCreated function, you could use Tracker.autorun to specify a function that will be rerun when the SourceElements collection changes (that's what all template helpers do behind the scenes):
Tracker.autorun(function() {
var element = SourceElements.findOne({'_id': data.source_id});
console.log(element);
});
This function will be called immediately. At this point, findOne will probably return undefined because the subscription is not ready yet. Once the data has arrived, the function will be called again and you can process the returned elements.
Ok this is what I've got.
The collection called Posts has content and I want to publish this under the name Merchs, the find() in the publish-function finds data but that is not shared to the client where Merchs is always empty.
//shared
Merchs = new Meteor.Collection('merchs');
// Posts has data I want to publish as "Merchs"
this.Posts = new Meteor.Collection('posts');
//server
Merchs.allow({
insert: function(userId, doc) {
return true;
},
update: function(userId, doc, fields, modifier) {
return true;
},
remove: function(userId, doc) {
return true;
}
});
Meteor.publish('merchs', function(data) {
return Posts.find();
});
//client
Deps.autorun( function() {
Session.get('selectedCategories');
subs.subscribe('merchs');
});
When creating your collection, the name in parentheses should be the name of the Mongo collection.
Merchs = new Meteor.Collection('merchs');
Should be:
Merchs = new Mongo.Collection('Posts');
That is, unless you already have a Posts variable defined in code that you didn't show. If you've already defined Posts and you're just looking to make another subscription to the same collection then you don't need this line at all:
Merchs = new Meteor.Collection('merchs');
You also don't need your allow() method (you can just use the one defined for Posts). All you need is the publish() method that you defined.
On the client side you also need:
Meteor.subscribe('merchs');
Also note the use of Mongo.Collection instead of Meteor.Collection which was renamed in Meteor 0.9.1.
You might want to read this excellent answer regarding publish/subscribe: https://stackoverflow.com/a/21853298/4665459
I just got done with the rough draft of my app, and thought it was time to remove autopublish and insecure mode. I started transfering all the stray update and insert methods I had been calling on the client to methods. But now I'm having trouble returning a username from an ID.
My function before: (that worked, until I removed autopublish)
challenger: function() {
var postId = Session.get('activePost');
var post = Posts.findOne(postId);
if (post.challenger !== null) {
var challenger = Meteor.users.findOne(post.challenger);
return challenger.username;
}
return false;
}
Now what I'm trying:
Template.lobby.helpers({
challenger: function() {
var postId = Session.get('activePost');
var post = Posts.findOne(postId);
if (post.challenger !== null) {
var userId = post.challenger;
Meteor.call('getUsername', userId, function (err, result) {
if (err) {
console.log(err);
}
return result;
});
}
return false;
},
Using:
Meteor.methods({
getUsername: function(userId) {
var user = Meteor.users.findOne({_id: userId});
var username = user.username;
return username;
},
...
})
I have tried blocking the code, returning values only once they're defined, and console.logging in the call-callback (which returned the correct username to console, but the view remained unchanged)
Hoping someone can find the obvious mistake I'm making, because I've tried for 3 hours now and I can't figure out why the value would be returned in console but not returned to the template.
Helpers need to run synchronously and should not have any side effects. Instead of calling a method to retrieve the user, you should ensure the user(s) you need for that route/template are published. For example your router could wait on subscriptions for both the active post and the post's challenger. Once the client has the necessary documents, you can revert to your original code.
I want to get data from a number of queries on the same collection, and unfortunately this is not yet supported on meteor. That's why I tried to do something like this:
Common
Dep = new Deps.Dependency;
Server
Meteor.methods({
fetch: function(){
var results = Data.find(dataQuery).fetch();
var otherResults = Data.find(queryThatCannotBeCombinedWithPrevious).fetch();
return results.concat(otherResults);
},
save: function(data){
Data.insert(data);
Dep.changed();
}
update: function(data){
Data.update({_id: data._id}, data);
Dep.changed();
}
});
Client
Session.setDefault('combinedData', []);
Template.demo.data = function(){
Dep.depend();
Meteor.call('fetch',function(error, data){
Session.set('combinedData', data);
});
return Session.get('combinedData');
};
This doesn't work though, propably because the Dep variable on the client is different from the Dep on the server. Is there a way to make the method call reactive when the contents of the Data collection change?
Notes
I am currently using Meteor 0.8.1.1, which doesn't allow subscriptions that return multiple cursors of the same collection yet.
This requires a small hack and you're close. First, you need a client–only dependence, the server just passes a data returned from the method and doesn't share variables (also there's nothing on the server that requires deps). Second, you only want to fetch the actual variable once, otherwise you'll end up with an infinite loop.
Example implementation:
var value = null;
var valueInitialized = false;
var valueDep = new Deps.Dependency();
Template.demo.data = function() {
valueDep.depend();
if(!valueInitialized) {
valueInitialized = true;
Meteor.call('fetchData', function(err, result) {
value = result;
valueDep.changed();
});
}
return value;
};
I am trying to generate a Flickr url based on a Flickr API call, and then return that result to a handlebars.js template. I am struggling to find a way around asynchronous processes.
I have tried to create a callback function, but I am still uncertain how to get a defined object or variable into the HTML template.
Here is the code for the Flickr API function:
var FlickrRandomPhotoFromSet = function(setID,callback){
Meteor.http.call("GET","http://api.flickr.com/services/rest/?method=flickr.photosets.getPhotos&api_key="+apiKey+"&photoset_id="+setID+"&format=json&nojsoncallback=1",function (error, result) {
if (result.statusCode === 200)
var photoResult = JSON.parse(result.content);
var photoCount = photoResult.photoset.total;
var randomPhoto = Math.floor((Math.random()*photoCount)+1);
var selectedPhoto = photoResult.photoset.photo[randomPhoto];
var imageURL = "<img src=http://farm"+selectedPhoto.farm+".staticflickr.com/"+selectedPhoto.server+"/"+selectedPhoto.id+"_"+selectedPhoto.secret+"_b.jpg/>";
FlickrObject.random = imageURL;
}
if (callback && typeof(callback)==="function") {
callback();
}
});};
My template code is this:
Template.backgroundImage.background = function(){
FlickrRandomPhotoFromSet(setID,function(){
return FlickrObject;
});
};
But this still leaves me stuck, not able to get a defined object into my HTML, which is coded as such:
<template name="backgroundImage">
<div id="background">
{{random}}
</div>
Use Session as an intermediary. It is reactive so as soon as its set it will change the template with the new data:
Template.backgroundImage.background = function(){
return Session.get("FlickrObject");
};
Template.backgroundImage.created = function() {
FlickrRandomPhotoFromSet(setID,function(){
Session.set("FlickrObject", FlickrObject)
});
}
So the created method will be run when the template is created to run FlickrRandomPhotoFromSet, when the result is returned it will set the Session hash which in turn will set the background as soon as the result is received.
Be careful with your FlickrRandomPhotoFromSet too, I didn't notice you had an argument for FlickrObject to pass to the callback.