Context : I am using a Collection Params to call method from the Server to a C app. The C app does its stuff and then calls the server by RPC to send me the results. With the result, I get the Params ID to delete the corresponding element.
With the deletion of the Element of Params, the C app gets a removed message. I want to prevent this behavior to avoid overloading the C app of messages.
I've thinked about implementing the removed event into the Publish method on the server to prevent the server from informing the C app. I just want the C app to be inform about added events.
On the Meteor Doc, there is an example of implementation of added and removed but I don't understand it. Can someone help me ?
I've tried this (don't work at all) :
Meteor.publish('expert_mode_parameters', function ()
{
var self = this;
var handle = Expert_Mode_Parameters.find().observeChanges({
added: function ()
{
return Expert_Mode_Parameters.find();
},
removed: function ()
{
return [];
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
}
It looks like your goal is to subscribe to a data set but only receive added messages, not changed or removed.
The below code should do this:
Meteor.publish('expert_mode_parameters', function () {
var self = this;
var handle = Expert_Mode_Parameters.find().observe({
added: function (document) {
self.added("expert_mode_parameters", document._id, document);
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
}
The concept is, you're watching the results of Expert_Mode_Parameters.find() and then calling self.added(document) when there is a new item. The same thing can easily be expanded to include changed.
Related
I have this piece of code in client side:
Tracker.autorun(function () {
if (params && params._id) {
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
}
}
});
params will be passed into the url. So, initially we won't have the department data and the findOne method will return null, and then later on, when data arrives, we can find the department object.
But if user enters an invalid id, we need to return them 404. Using tracker autorun, how can I distinguish between 2 cases:
a. Data is not there yet, so findOne returns null
b. There is no such data, even in server's mongodb, so findOne will also returns null.
For case a, tracker autorun will work fine, but for case b, I need to know to return 404
I would suggest you to subscribe to data inside template, like below so you know when subscriptions are ready, then you can check data exists or not
Template.myTemplate.onCreated(function onCreated() {
const self = this;
const id = FlowRouter.getParam('_id');
self.subscribe('department', id);
});
Template.myTemplate.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
const id = FlowRouter.getParam('_id');
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
// found data in db
} else {
// 404 - no department found in db
}
}
});
If you are using Iron-Router, you may try this hack.
Router.route('/stores', function() {
this.render('stores', {});
}, {
waitOn: function() {
return [
Meteor.subscribe('stores_db')
];
}
});
The sample code above will wait for the subscription "stores_db" to complete, before rendering anyhing. Then you can use your findOne logic no problems, ensuring that all documents are availble. This suits your situation.
This is what I used to do before I completely understand MeteorJS publications and subscriptions. I do not recommend my solution, it is very bad to user experience. Users will see the page loading forever while the documents are being download. #Sasikanth gave the correct implementation.
Here is my template onCreated and helper function. I have provided inline comments to add some clarity. I can see that self.post is eventually set because it shows up on my console when I log Template.instance(). However, when I log Template.instance().post, it is always undefined.
Template.default.onCreated(function() {
var self = this;
self.autorun(function() {
var postId = FlowRouter.getParam('post_id');
self.subscribe('onePost', postId);
self.post = Posts.findOne(FlowRouter.getParam('post_id'));
});
});
Template.default.helpers({
poststage: function(stage) {
console.log(Template.instance()); // I can see post object here
console.log(Template.instance().post; //always undefined
if(Template.instance().post) {
// never true
return Template.instance().post.stage == stage;
}
}
});
Template.default.events({
'submit form':function(event, instance) {
Meteor.call('someFunc', instance.post.anotherField);
}
});
Edit: I should add, I'm trying to avoid writing the query twice because I'm also using it in the Template events. (see code).
This feels like a timing issue. You are subscribing to the onePost publication but you aren't waiting for that subscription to be .ready() before assigning self.post The autorun also seems superfluous. You shouldn't need to rerun that block of code in the onCreated block. I suggest:
Template.default.onCreated(function() {
this.postId = FlowRouter.getParam('post_id');
self.subscription = subscribe('onePost', this.postId);
});
Template.default.helpers({
poststage: function(stage) {
if ( this.subscription.ready() ){
return Posts.findOne(this.postId).stage;
}
}
});
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.
I'm trying to do some validation prior to loading the main page. To do this I need to find a document that I have confirmed, exist in the Mongo Collection. Unfortunately finding the document in the client.js doesn't seem to work. In my opinion the client and server collection are not in sync. Based on similar articles i have read I made many changes without success. Here is a quick summary of what I have tried.
Option1: Try to find the record in the client side and not using auto-subscribe: record not found.
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.findrec(credentialToken);
console.log("results:",results); //results is undefined.
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
Meteor.findrec = function(credentialToken) {
target = {credentialtoken:credentialToken};
recfound = crs_collection.findOne(target);
//No luck with find either.
//recfound = crs_collection.find({credentialtoken:credentialToken}, {limit:1}).fetch()[0];
console.log("recfound:",recfound); //returns recfound is undefined.
return recfound;
}
In /server/server.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
Option2: Next I did the find in the server side, using a method "server_recfind" which worked but I'm not able get the content to the client.
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.call('server_findrec',credentialToken);
console.log("results=",results); // also returns undefined
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
In /server/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
// Using Sync which finds the record but how do I sent the content to the client?
Meteor.methods ({
'server_findrec': function(credentialToken) {
// tried unblock but didnt work
//this.unblock();
var rec = crs_collection.findOne({'credentialtoken': credentialToken});
console.log("INSIDE server findrec rec=",rec); //shows content found
// tried flush but it didn't do anything
crs_collection.flush;
return rec; //rec not returning to the client
}
})
Option3: Frustrated and since I was able to find the document record with the server method. I tried adding global variables to delivery the content to the client side. Unfortunately it didn't work
In app.js
credentialToken = "2KcNCRzpTHzyZ1111";
//added global variables
c1 = '';
c2 = '';
c3 = ''
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to ares_sso.";
};
Meteor.startup(function () {
var results = Meteor.call('server_findrec',credentialToken);
console.log("results=",results); // also returns undefined
console.log("c1=",c1);
console.log("c2=",c2);
console.log("c3=",c3);
});
Template.hello.events({
'click input': function () {
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
In /client/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.subscribe("crs");
In /server/app.js
crs_collection = new Meteor.Collection("crs");
Meteor.publish("crs", function(){
return crs_collection.find();
});
// Using Sync which finds the record but how do I sent the content to the client?
Meteor.methods ({
'server_findrec': function(credentialToken) {
// tried unblock but didnt work
//this.unblock();
var rec = crs_collection.findOne({'credentialtoken': credentialToken});
console.log("INSIDE server findrec rec=",rec); //shows content found
c1 = rec.cont1;
c2 = rec.cont2;
c3 = rec.cont3;
//confirm that c1,c2 and c3 have content
console.log(In server_findrec c1=",c); //shows content
console.log(In server_findrec c2=",c2); //shows content
console.log(In server_findrec c3=",c3); //shows content
// tried flush to sync to client...didn't work
crs_collection.flush;
return rec; //rec not returning to the client
}
})
There is a lot more code, so I have assembled all of the above hoping it gives you a clear picture of what I have tried and what I'm trying to do. I'm sorry if I made a mistake in the process.
Overall it will be great to know what am I doing wrong? I believe the 3 scenarios should work. Any help or recommendation will be appreciated.
I'm using Meteor Release 0.7.1.2, no CoffeeScript.
Thank you all
You're making two mistakes:
Define crs_collection once, and make sure its in a file thats executed on the client AND the server. It should be defined globally.
crs_collection must be defined before your pub/sub code. Meteor executes files in the lib directory first, so its best to put your collection code there.
That's really all there is to it. I'm happy to provide an example if needed.
I've been trying to access the this.userId variable from within a Meteor.methods call, but it doesn't seem to work when I try to call the method via Meteor.setTimeout or Meteor.setInterval.
This is what I've got:
if (Meteor.is_server) {
Meteor.methods({
getAccessToken : function() {
try {
console.log(this.userId);
return Meteor.users.findOne({_id: this.userId}).services.facebook.accessToken;
} catch(e) {
return null;
}
}
});
var fetch_feed = function() {
console.log(Meteor.call("getAccessToken"));
[...] // A bunch of other code
};
Meteor.startup(function() {
Meteor.setInterval(fetch_feed, 60000); // fetch a facebook group feed every minute
Meteor.setTimeout(fetch_feed, 3000); // initially fetch the feed after 3 seconds
});
}
Watching the terminal log, the this.userId always returns a null. But if I try calling the method from the client side, or through the console, it returns the correct ID.
How come this doesn't work from within a Meteor.setInterval? Is it a bug or am I doing something wrong?
Meteor userId's are associated with client connections. The server may interact with many clients and this.userId inside a method will tell you which client has asked for the method to be run.
If the server uses Meteor.call() to run a method then it will not have a userId since it is not running for any client.
The methods allow clients to call for functions to be run on the server. For things the server will trigger itself a javascript function will do.
There is a solution I used - sometimes you do not want to make the method a function but really want it to remain a method. In that case, a hack to make this work:
var uniqueVar_D8kMWHtMMZJRCraiJ = Meteor.userId();
Meteor.setTimeout(function() {
// hack to make Meteor.userId() work on next async
// call to current method
if(! Meteor._userId) Meteor._userId = Meteor.userId;
Meteor.userId = function() {
return Meteor._userId() ||Â uniqueVar_D8kMWHtMMZJRCraiJ
};
Meteor.apply(methodName, args);
}
, 100);
Some brief explanation: we save Meteor.userId in Meteor._userId and overwrite Meteor.userId with a function that returns Meteor._userId() if it is true and otherwise the historic value of Meteor.userId() before any of this happened. That historic value is saved in an impossible to occur twice var name so that no context conflicts can happen.