Unable to invoke Meteor.call from Client in MeteorJS? - meteor

Using meteor 0.5.7 - following the Parties example - made client, server folders, and placed respective client.js and server.js files in them. I have autopublish, insecure removed, and added email package. I can't get the Meteor.call to fire off, debugging shows that it gets bypassed, I'm loosely following this - http://goo.gl/MV26m - and I still don't understand.
// server.js
Meteor.startup(function () {
process.env.MAIL_URL = '...'; // using mailgun URL
});
Meteor.methods({
call_me: function (options) {
var options = options || {};
Email.send({
from: options.from,
to: options.to,
replyTo: options.from || undefined,
subject: options.subj,
text: options.msg,
});
},
});
// client.js
Template.form.events({
'click .submit' : function (event, template) {
var from = template.find("#from").value;
var to = template.find("#to").value;
var subj = template.find("#subj").value;
var msg = template.find("#msg").value;
var options = { from: from, to: to, subj: subj, msg:msg };
Meteor.call('call_me', options, function(err, data) {
if (err)
console.log(err);
});
}
});
// client.html - the gist of it
<body>
{{> page }}
</body>
<template name="page">
{{> form }}
</template>
<template name="form">
<form ....
</template>
Lastly, I actually had the Meteor.methods({...}); sitting in a model.js file outside the client/server folders - and it still didn't fire off emails, or invoke the Meteor.call method. I'm kinda trying to wrap my head around the notion of a stub in the aforementioned attached link, wrapped the call within a function and called it, and I still didn't get any activity. Thanks for any advice.

Tried your gist. Removing the <form> tag and commenting out Process.env.MAIL_URL did it. The <form> tag is blocking the event trigger to button click.

Related

Json page with collection data

i am very new to nodejs and meteor. i need to create a page content-type application/json and data from mongo collection. So when collection data change json page must be change.
For json page i use this example:
https://stackoverflow.com/a/23666992/1446182
if (Meteor.isServer) {
Meteor.startup(function () {
try{
var interval = Meteor.setInterval(function() {
var result = Meteor.http.call("GET", "http://192.168.2.144//ihale/meteorGetPage" );
var resultJson = JSON.parse(result.content);
var json = IhaleCollection.findOne();
IhaleCollection.update(json, {$set: {json: resultJson}});
}, 1000);
}
catch(err) {
console.log(err);
}
});
Router.map(function() {
this.route('jsonExample', {
where: 'server',
path: '/json',
action: function() {
var obj = IhaleCollection.findOne();
var headers = {'Content-type': 'application/json'};
this.response.writeHead(200, headers);
this.response.end(JSON.stringify(obj));
}
});
});
}
When Meteor.startup i start to update IhaleCollection every second.
So how i can update json page when IhaleCollection change?
You can't. Not this way anyway. JSON has no builtin mechanism to detect if the source has changed and given that the router endpoint is outputting raw JSON data with no javascript, no automatic updates will happen. Only when a client refreshes the endpoint will there be an update.
Meteor deals with reactive data by inserting javascript in the underlying html page. If you want to utilize that then you have to write a template, write a helper method to return JSON and use the helper method in the body.
The helper method should look something like this:
Template.jsondoc.helpers{(
json: function(){
return JSON.stringify(IhaleCollection.findOne());
}
})
Template can be as simple as that:
<template name="jsondoc">
{{json}}
</template>
and your route will be as simple as this:
Router.route('/jsondoc');

access data from iron-router in rendered function

I'm trying to access data passed from iron router in the javascript function
router.js
this.route('editOrganization', {
path: '/editOrganization',
waitOn: function() {
return [
Meteor.subscribe('organization', this.userId)
];
},
data: function() {
return Organizations.findOne();
}
});
now if I wanted to access a property of organization in html (editCompany.html) I can do the following
{{name}}
but how do I access that same property in the js file
Template.editOrganization.rendered = function() {
//how do I access name?
}
UPDATE:
so if I click a link to edit organization I can get the value via
this.data.name
However, if I reload the page (same url) it throws an error saying data is null.
It is accessible through the rendered function context.
Template.editOrganization.rendered = function() {
var name = this.data && this.data.name;
};
This is confusing for many people but you need to configure the router to actually wait for the subscriptions you returned with waitOn.
Router.onBeforeAction('loading')
You can read the author's explanation here:
https://github.com/EventedMind/iron-router/issues/554#issuecomment-39002306

meteor reactivity breaks when using server/client folders

So I made something super simple to test out the reactivity in Meteor but when I came to make a server and client folder the reactivity broke. I can no longer manually edit the database and see the change instantly in the browser.
Template:
<template name="hello">
<input type="button" value="Click" />
{{#each tt}}
{{test}}
{{/each}}
</template>
client/test.js:
Template.hello.events(
{
'click input': function ()
{
Meteor.call('set');
}
});
Template.hello.helpers(
{
tt: function()
{
Meteor.call('get', function(error, result)
{
Session.set('aa', result);
});
return Session.get('aa');
}
});
server/testS.js:
Test = new Meteor.Collection("test");
Meteor.methods(
{
set: function()
{
Test.insert({test: "test 1"});
},
get: function()
{
return Test.find().fetch();
}
});
What am I missing to get reactivity using this folder structure?
The following is an issue.
Meteor.call('get', function(error, result) {
Session.set('aa', result);
});
This only occurs once in your case. Meteor.call is generally meant as a singular request, and is completely different than the publication/subscription model. The only "reactivity" you would experience in this case is if you manually do Session.set('aa', result);
If you want reactivity between the client/server DB, you need to set up publication/subscription code (see http://docs.meteor.com/#meteor_publish). By default, all documents in the database are published to the client via the auto-publish package, so keep that in mind. This is to auto-allow you do stuff like Collection.find() on the client, which returns a cursor, and is reactive by default.
In other words, your Meteor.call is redundant. The Test collection already exists on the client, allowing you to do the following.
Template.hello.helpers({
tt: function() {
return Test.find();
}
});

using angularjs http-auth with $resource

I am trying to make HTTP Auth Interceptor Module work together with $resource.
for now, i have a basic app working with services like this :
angular.module('myApp.services', ['ngResource']).
factory('User', function($resource){
return $resource('path/to/json/', {}, { 'login': { method: 'GET' } });
});
and then controllers like this one :
angular.module('myApp.controllers', []).
controller('User', ['$scope', 'User', function($scope, List)
{ $scope.user = User.query();
]);
and the app :
angular.module('myApp', ['myApp.services', 'myApp.directives', 'myApp.controllers']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/dashboard', {templateUrl: 'partials/dashboard.html', controller: 'Dashboard'});
$routeProvider.when('/user', {templateUrl: 'partials/user.html', controller: 'TrackingCtrl'});
$routeProvider.otherwise({redirectTo: '/dashboard'});
}]);
Until now, everything is working like expected. Then, i put an http auth on the server, so the json files http status are 401 and the browser display a login popup. I would like to use the HTTP Auth Interceptor Module to replace the browser login popup and handle the login. it is the purpose of this script, right ?
To do so, i am trying to understand the demo of the script and make that work with my app.
first, i injected 'http-auth-interceptor' to the app.
then, this was added to index.html
<body ng-controller="UserCtrl" class="auth-demo-application waiting-for-angular">
and the login form above ng-view:
<div id="login-holder">
<div id="loginbox">
<div id="login-inner" ng:controller="LoginController">
<form ng-submit="submit()">
Username: <input type="text" class="login-inp" ng-model="username"/><br/>
Password: <input type="password" class="login-inp" ng-model="password"/><br/>
<input type="submit" class="submit-login"/>
</form>
</div>
</div>
</div>
and the script at the bottom of the page:
<script src="lib/directives/http-auth-interceptor.js"></script>
in controller.js :
controller('LoginController', ['$scope', 'authService', 'User', function ($scope, authService, User) {
$scope.submit = function() {
User.login(
function(user, response) { // success
authService.loginConfirmed();
console.log('login confirmed');
}
);
}
}])
and this directive too :
directive('authDemoApplication', function() {
return {
restrict: 'C',
link: function(scope, elem, attrs) {
//once Angular is started, remove class:
elem.removeClass('waiting-for-angular');
var login = elem.find('#login-holder');
var main = elem.find('#content');
login.hide();
scope.$on('event:auth-loginRequired', function() {
login.slideDown('slow', function() {
main.hide();
});
});
scope.$on('event:auth-loginConfirmed', function() {
main.show();
login.slideUp();
});
}
}
})
Ok… that's it. but it's not working at all: the browser loggin form is still there. and that's the only way to login.
any idea on what i should do to make this work ?
From their webpage:
Typical use case:
somewhere the: $http(...).then(function(response) { do-something-with-response }) is invoked,
the response of that requests is a HTTP 401,
'http-auth-interceptor' captures the initial request and broadcasts 'event:auth-loginRequired',
your application intercepts this to e.g. show a login dialog (or whatever else),
once your application figures out the authentication is OK, you are to call: authService.loginConfirmed(),
your initial failed request will now be retried and finally, the do-something-with-response will fire.
So the only thing you need to do to interact with this is to have a parent scope to listen to even:auth-loginRequired. This can be done in a directive, in a controller, it doesn't matter. I haven't used the software, but I'm imagining something like this:
angular.module('myApp', [])
.service('api', function(httpAutenticationService, $rootScope, $location) {
$rootScope.$on('event:auth-loginRequired', function() {
// For the sake of this example, let's say we redirect the user to a login page
$location.path('/login')
})
$http.get(API_URL).then( ... ); // If the user is not authenticated, this will be intercepted (1)
})
.controller('LoginController', function(httpAutenticationService) {
// Let's imagine you have a route with the 'controller' option to this controller
$http.get(LOGIN_URL).then(function() {
httpAutenticationService.loginConfirmed(); // The success callback from (1) will be executed now
}, function(){
alert("Invalid credentials")
})
})

Invoke a client js function in Meteor after getting results from the server

I'm trying to see how can I invoke a js function after the client gets a result from a Meteor method call. The only thing I was able to get is to invoke the function myFunc only on the client that made the actual method call.
Any thoughts how i can invoke the function on all the currently subscribed clients?
here is the code:
function myFunc(error, result) {
alert(result);
}
if (Meteor.is_client) {
Template.container.events = {
'click input' : function () {
Meteor.call('someMethod',myFunc);
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
// code to run on server at startup
});
}
Meteor.methods({
someMethod: function() {
//console.log(!this.is_simulation);
return "something";
}
})
Thanks
Currently you can't broadcast a method call to all clients directly. At least as far as I can tell. But a work around would be to create a collection called Alerts and monitor it for changes. Then when you want to send a message to all your users you can change the document in Alerts:
Client:
Alerts = new Meteor.Collection("alerts")
Meteor.autosubscribe(function() {
Alerts.find().observe({
added: function(item){
alert(item.message);
}
});
});
Server:
Alerts = new Meteor.Collection("alerts")
Meteor.publish("alerts", function(){
Alerts.find();
});
Alerts.remove({}); // remove all
Alerts.insert({message: "Some message to show on every client."});
Another option is using Meteor Stream package which purpose is to avoid using a mongodb collection on the server side. It does supports client to clients, server to clients, client to server AND server to servers messaging, including a support for Meteor Cluster
If you want to stay with meteor only using collections, the following code allows you to either broadcast a message from the client to all the clients or a message from the server to all the subscribed clients. Just use this mechanism to then fire a function on the client side once the right message is received. The code is made in such a way that you will never have useless items remaining into the collection.
Messages = new Meteor.Collection("messages");
if (Meteor.isClient) {
Meteor.subscribe("messages");
var query = Messages.find({});
var handle = query.observe({
added: function(document)
{
console.log(document.message);
}
});
// Test the mechanism from the client side
Meteor.call("client talked");
}
if (Meteor.isServer) {
Meteor.startup(function() {
Messages.remove({});
});
Meteor.publish("messages", function()
{
// you might add an optional filter in order to broadcast only the messages you want to the client
return Messages.find();
});
function talk(message)
{
var id = Messages.insert({"message":message});
Messages.remove(id);
}
Meteor.methods(
{
talk: function(message)
{
// you might filter here if the clients can talk using this.userId
talk(message);
}
});
// test the mechanism from the server side
talk("server talked");
}
I like what Zeke said, but for people who uses Meteor 0.5.0+, use Deps.autorun instead of autosubscribe... details at:
https://groups.google.com/forum/#!topic/meteor-core/mTa81RLvhbY
and
http://www.meteor.com/blog/2013/02/14/meteor-055-devshop-code-and-community-contributions
I simple approach to call a JavaScript client-side function would be to add a script tag in your html template that is bound by your collection. Anytime a new item is inserted, this tag would be inserted into the client would run your function. I have a collection call uploads with some properties such as name. The following template triggers drawpoints() client-side function upon receipt of a new item in Uploads collection:
{{#each uploads}}
<tr>
<td>{{name}}</td>
<td>
<div class="alert alert-success">Download Here</div>
</td>
</tr>
<script>drawpoints();</script>
{{/each}}

Resources