I understand that Meteor methods let you do a client to server call, but what's the best approach to call another function or method from a Meteor method, i.e. a server to server call.
Right now if I do a regular JS function call it only works if the JS file is in the lib folder. But I need it to be in the server folder.
Here is the code
I have a topics collection which sits in the collection folder and has the following
I have the following which is a collection
Meteor.methods({
topicPost: function(topicAttributes) {
var user = Meteor.user(),
topicWithSameTitle = Topics.findOne({title: topicAttributes.title});
// ensure the user is logged in
if (!user)
throw new Meteor.Error(401, "You need to login to add a new topic");
Meteor.call('checkUser');
}
});
I then have the following method which sits in the server folder
Meteor.methods({
checkUser: function () {
alert('aaaa');
}
});
This works, but it's not a great solution. My method for handling this is to have all of my functions outside the Meteor.methods, and simply relay to the proper functions when necessary.
// Client
Meteor.call('foo');
And:
// Server
Meteor.methods({
foo: function() {
foo();
}
});
foo = function() {
foo = bar;
};
The advantage is that the foo fn can be called from anywhere on the server without a Meteor.call. Meanwhile, Meteor.methods only exposes what is absolutely necessary to the client.
[EDIT] There is some ambiguity as to which 'foo' you're talking about; obviously the server knows you mean the one outside the methods call. But if you're feeling confused, you can always rename one or the other. The advantage to this is that there is minimal refactoring involved.
Just to clarify for readers who don't notice that the OP's code actually contains the answer, you just do
Meteor.call('checkUser');
on the server. Per the meteor docs (https://docs.meteor.com/api/methods.html#Meteor-call), on the server, if you use Meteor.call() without a callback argument, the call runs synchronously and waits for the result. For example, if 'checkUser' was written to provide a userId value, you'd just do
let userId = Meteor.call('checkUser');
On the client, though, you have to provide a callback function as an argument to Meteor.call(), and the userId would be provided asynchronously to your callback function.
Related
I have seen quite a few times that the startup function of Meteor returns a function. What does it mean? Where is the function returned? Who is using the returned function?
Meteor.startup(function () {
Init();
return Tracker.autorun(function () {
const userId = Meteor.userId();
if (!userId) {
//do something
}
});
});
I have not seen startup returning a function, and I have never used it. It wont break and will work all the same, but I do not know why it is done in this example.
It is also strange that it is returning an autorun function... both startup and autorun are client api event handler hooks ... no one should be listening to the results of the function calls becuase these calls are not made by the app but rather the meteor platform, their function is to run code at either startup, or as a separate reactive fiber (autorun).
I want to define a server-side Meteor method that, when called, will run a function on the client.
Ultra simple example below - I want this method to take a parameter, and when called, will run console.log(parameter) on the client side console.
Meteor.methods({
consoleLogOnClient: function(text){
var log = function(){
console.log(text);
};
return log();
}
});
But when I do:
Meteor.call('consoleLogOnClient', 'THIS MESSAGE SHOULD APPEAR ON THE CLIENT CONSOLE');
the message gets logged in the server console and nothing appears in the client console.
Ok, fair enough. Maybe I'll just return the function code itself and store that in a variable and then run it. But it doesn't work either.
Meteor.methods({
consoleLogOnClient: function(text){
var log = function(){
console.log(text);
};
return log;
}
});
var myFunction = Meteor.call('consoleLogOnClient', 'THIS MESSAGE SHOULD APPEAR ON THE CLIENT CONSOLE');
myFunction();
http://docs.meteor.com/#meteor_methods
They should return an EJSON-able value or throw an exception.
EJSON: http://docs.meteor.com/#ejson
So, the answer is that you can't send a function from the server to the client, since functions aren't EJSON values. But your real problem is probably that you want to send a function; I can't imagine why you want to do that. Send data instead.
You can achieve a desired result with the anticoders:client-call package. It allows you to define client-side methods that you'd be able to run from the server side.
For example, if you define:
Meteor.ClientCall.methods({
'consoleLog': function(message) {
console.log(message);
},
});
And set userId as clientId for the methods:
Deps.autorun(function() {
Meteor.ClientCall.setClientId(Meteor.userId());
});
Then on the server side you can simply call:
Meteor.ClientCall.apply(userId, 'consoleLog', ['THIS MESSAGE SHOULD APPEAR IN THE CLIENT CONSOLE']);
When trying to render my template, i want to load the data from the server. I'm trying to use Meteor.call but as per the documentation, i'm clearly not in a stub.
If I use Meteor.call inside of an event handler, the response i get back is correct. If i call it within the template.created or similar, i get an undefined response. I guess i could use async call to do it and then render it when available. But is there another way?
I don't want the clients to have direct access to the DB, i want it to come from the server.
//This doesn't work
Template.config.created = function() {
console.log(Meteor.call('getValue')); //returns undefined
};
//This works
Template.config.events({
'blur #button' : function () {
console.log(Meteor.call('getValue')); //Prints value
}
Any clues?
D
You need to use a callback in your Meteor.call
Template.config.created = function() {
Meteor.call('getValue', function(error, data) {
if(error){
//do stuff to handle error
}
console.log(data);
});
};
From the docs:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
I'm not sure why your event handler call is working... There isn't any way to synchronously get a server response like that in JavaScript without Fibers. The solution is simply to provide an asynchronous callback. This isn't really a Meteor limitation, it's just a JavaScript limitation.
I am trying to run certain insert statements after a collection has been updated. For example, if a user adds an embedded document Location to their User document, I would like to also insert that embedded document into a separate Location collection. Is there a way to do this on the server side so that the operation is guaranteed to run?
If you're willing to use some code I wrote (https://gist.github.com/matb33/5258260), you can hook in like so:
EDIT: code is now part of a project at https://github.com/matb33/meteor-collection-hooks
var test = new Meteor.Collection("test");
if (Meteor.isServer) {
test.before("insert", function (userId, doc) {
doc.created = doc.created || Date.now();
});
test.before("update", function (userId, selector, modifier, options) {
if (!modifier.$set) modifier.$set = {};
modifier.$set.modified = Date.now();
});
test.after("update", function (userId, selector, modifier, options, previous) {
doSomething();
});
}
you would need to do it in a method.. you can keep latency compensation by implementing a client-side Method stub:
Calling methods on the client defines stub functions associated with
server methods of the same name. You don't have to define a stub for
your method if you don't want to. In that case, method calls are just
like remote procedure calls in other systems, and you'll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will
also run its stub in parallel. On the client, the return value of a
stub is ignored. Stubs are run for their side-effects: they are
intended to simulate the result of what the server's method will do,
but without waiting for the round trip delay. If a stub throws an
exception it will be logged to the console.
see my Meteor stub example here: https://stackoverflow.com/a/13145432/1029644
I would like to use the GetLocalizedItems method in the Anguilla Framework.
I don't know how to create a new filter and set the conditions or what to use for success and failure.
In the GUI:
tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(id, filter.conditions,
filter.columns, success, failure);
Are the methods in this namespace intended to be used by our extensions?
Building a filter
Here is an example of how to build a filter
var filter = new Tridion.ContentManager.ListFilter();
filter.conditions.ItemTypes = 16 | 2; // folders and components
filter.conditions.Recursive = true;
filter.conditions.BasedOnSchema = "tcm:1-23-8,tcm:1-32-8".split(",");
filter.columns = Tridion.Constants.ColumnFilter.DEFAULT;
Or this extremely simple case from General.js:
var templateFilter = new Tridion.ContentManager.ListFilter({
conditions: { ItemTypes: [ itemType ] }
});
Calling a WCF method
The second part of your question was really already covered in https://stackoverflow.com/a/9385975/209103, although I'll make it a bit more concrete here.
WCF/AJAX calls such as this are executed asynchronously, since they may take some time to complete. While you would normally simple handle the result of the call on the line after the closing parenthesis, you can't do that in AJAX calls since that line will be execute before the function has completed. Instead you have to pass in one or more callback functions that get called once the function completes.
I typically just pass in two functions that break into the JavaScript debugger of my browser when I first start figuring out such a method:
Tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(
"tcm:1-23",
filter.conditions,
filter.columns,
new function() { console.log(arguments); debugger; },
new function() { console.log(arguments); debugger; }
);
So the first (anonymous) function is called when the (asynchronous) HTTP call to the TCM server succeeded, while the second is called when the call failed. In the answer I linked above, we called them onSuccess and onFailure to make their nature more explicit.
Both functions in this case simply write the implicit arguments parameter that is always passed in JavaScript. They then break into the JavaScript debugger of your browser, so you can inspect the arguments further.