I have a meteor method defined in ClassModel.js, which is located within /server. On the client js file, I set up a template event that tries to call this function, but the function keeps throwing a ReferenceError and is undefined. Any idea why?
Code:
client/client.js
Template.class_disc.events({
'click .pick_class': function (event) {
event.preventDefault();
var id = $(event.currentTarget).parent('div')[0].id;
var explo = id.split("\"");
var id = explo[0];
Meteor.call(findClassByID, id, function(err, res) {
console.log(res.content);
});
});
/server/classModel.js
Meteor.methods({
findClassByID: function(id) {
console.log('in findclassbyid')
return Classes.find({ _id: id }).fetch();
}
});
Could the problem be that I have multiple Meteor.methods({}) declarations across different server files? Help would be much appreciated.
I mostly just need to look at the Classes collection and verify the ID's I'm pulling match some in the database, for sanity. Might there also be a way to query/publish the whole classes database to the client so I can query it within the console?
lol wow, this turned out to be a syntax error.
When calling Meteor.methods, the method name needs to be in quotations!
Meteor.call("methodName", args, callback);
Related
Ghost Blog has limited functionality when it comes to outputting content to a post and it's typically done through the {{content}} helper. I am trying to add more nuanced capabilities to the content helper by creating my own handlebars helpers to output blocks of content from within the ghost {{content}} helper.
I've been making use of these two resources to create my own solutions to the problem https://www.neoito.com/ghost-cms-on-steroids-with-custom-block-helpers/
https://github.com/TryGhost/Ghost/wiki/Apps-Getting-Started-for-Ghost-Devs
Everything works fine for the most part, but I've hit a snag when trying to port html from the post {{content}} to the handlebars helper I've registered. I managed to make an ajax call using jquery from the back-end by npm installing it within the registered helper folder. The folder is set up via the method described in the second link (creating an App within the ghost content folder).
in the index file, the helper is stored in a separate function and called when the app activates. My problem is getting the helper function to accept the ajax call and extract the html from the returned value.
I'm not set on this method and there is a way that it has been done in the first link (however, it was created to solve the problem with an older versions of ghost - pre 1.0 and I'm making use of Ghost 1.2.0 so I'm aware of some "breaking changes" have been made).
I need a way to extract the post html from the server side of things (if its even possible). The internal api does not work for me unless it is called inside the activate function but I cant seem to get it to work in the helper function which is outside of the activate functions scope... I'd love some help on this.
Here my index file to give some context to this. If you need any more information, let me know and I'll post (right now I cant think of anything else you might need)
const $ = require('jquery');
var App = require('ghost-app'),
hbs = require('express-hbs'),
ghost_api = require('ghost/core/server/public/ghost-sdk'),
proxy = require('ghost/core/server/helpers/proxy'),
helpers_briefcase;
helpers_briefcase = App.extend({
// content: function () {$.get(ghost.url.api('posts', {formats:["html"]})).done(function (data){
// console.log('posts', data.posts["html"]);
// return ('posts', data.posts["html"])
// console.log('it worked');
// debug('it worked');
// }).fail(function (err){
// console.log(err);
// });
// },
//Filter handling
filters: {
ghost_head: 'handleGhostHead',
ghost_foot: 'handleGhostFoot'
},
handleGhostHead: function () {},
handleGhostFoot: function () {},
install: function () {
},
uninstall: function () {
},
//Register Handlebars Helpers on activate
activate: function (posts) {
//Test for getting post content
this.ghost.api.posts.read(0).then(function (post) {
console.log(post.title);
return post.title
});
this.ghost.helpers.register('content_block', this.content_block_helper);
this.ghost.helpers.register('if_eq', this.if_eq);
},
deactivate: function () {
},
content_block_helper: function(node, posts) {
var content = post.data.root.post.html;
var regexstring = '<content_block'+ node +'>[\\s\\S]*?<\/content_block'+
node + '>'
var regexp = new RegExp(regexstring);
if(content.match(regexp)){
var match = content.match(regexp)
match = match.replace('<content_block'+ node + '>', '');
match = match.replace('</content_block'+ node + '>', '');
return new hbs.SafeString(match)
} else {
return('My first Handlebars Helper');
}
},
if_eq: function(a, b, opts) {
if (a == b) {
return opts.fn(this);
} else {
return opts.inverse(this);
}
}
});
module.exports = helpers_briefcase;
Thanks in advance.
Disclaimer: I consider myself something between a dedicated hobbyist and an expert, so before you go pointing out what javascript sins I've committed, please keep this in mind (go easy with pointing out errors outside of the problem I'm trying to solve) *thanks S.O Community.
I am trying to work with data from a Collection in Angular-Meteor, but so far I fail to access it.
In lib/collections.js I define the collection:
UserMeta = new Mongo.Collection('userMeta');
server/publish.js publishes it:
Meteor.publish('userMeta', function() {
return UserMeta.find();
});
and in my client code client/scripts/controllers/settings.controller.js I subscribe:
angular
.module('App')
.controller('SettingsCtrl', SettingsCtrl);
function SettingsCtrl($scope, $reactive) {
$reactive(this).attach($scope);
this.subscribe('userMeta');
//...
}
I have discovered that there seem to be multiple ways to subscribe (even used inconsistently in the official tutorial from Angular-Meteor), but I have decided to use the most recent syntax for v.1.3.2: Subscribe API
But if I want to view the whole content of the collection, it returns an empty array:
console.log(UserMeta.find().fetch()); // =[]
Or:
console.log(UserMeta.findOne()); // =undefined
However, if I run these commands in the client console in my browser, the expected results are returned.
Can somebody please give me a short example, how I can work with my collections? I am used to the way (pure) Meteor handles this and am confused that it does not seem to be as simple in Angular-Meteor.
Try using
Meteor.Methods
On your Server side call
Meteor.methods({
getUserMeta: function () {
var data = UserMeta.find({}).fetch();
return data;
}
All call this method on server side using
Meteor.call('getUserMeta',function (err, data) {
if (!err) {
Console.log(data);
} else {
console.log("error");
}
});
When you use Collection in console.log it isn't ready yet and hasn't any data.
You can use console.log inside helpers or you should check if collection is ready like this:
// Client-side
var subs = Meteor.subscribe('lastMsgRead', Meteor.userId());
Meteor.autorun(function() {
if (subs.ready()) { ... }
});
I am trying to get a document from a collection, but it doesn't seem to be working.
when i use the find().fetch(), it returns only an empty array. my code is as follows.
var users = new Mongo.Collection("users");
console.log(users.find());
var userRecord = users.find().fetch();
var returnUserRecord = {};
if (userRecord.length >0){
returnUserRecord = {username:userRecord.username, loginHash:userRecord.loginHash};
console.log("if statement is not complete and the value of the return variable is");
console.log(returnUserRecord);
}
return returnUserRecord
I have checked the database directly and noticed that there is indeed a document in the collection with the command:
meteor mongo
if it makes any difference, all this code in the in the server js file, and is being called from from the client by: Meteor.Methods()/Meteor.call()
EDIT 1
i created another collections with new data from the client, and after selecting the correct database, and running the command:
meteor:PRIMARY> db.newCollection1.find()
i get:
{ "_id" : ObjectId("55d1fa4686ee75349cd73ffb"), "test1" : "asdasd", "test2" : "dsadsa", "test3" : "qweqwe" }
so this confirms that it is available in the database, but running the following in the client console, still doesnt return the result. (autopublish is installed. i tried removing autopublish and made the appropriate changes to subscribe to the table, but that didnt work either).
var coll = new Meteor.Collection('newCollection1');
coll.find().fetch()
this returned an empty array. i have also tried the same on the server.js code using:
meteor debug
but i am still getting an empty array. does anyone know what i might be doing wrong here?
SOLUTION
the solution for this was to create the collection variable in the Meteor object context. this way it can be accessed from the Meteor context.
i.e.
Meteor.coll = new Meteor.Collection('newCollection1');
Meteor.coll.find().fetch();
i hope this helps someone. depending on your code you may want to use a different context.
You don't wait for this subscription to complete, therefore you get empty array.
You should probably read this or this to better understand it.
The thing is you connect users variable to "users" collection, and when you call it, it isn't yet polluted with data (if you don't want to use subscription then maybe use helper - it's reactive so it will return proper value when subscrtiption is finished)
Did you subscribe your users collection somewhere?
if (Meteor.isServer) {
Meteor.publish("users", function(){
return Users.find({})
});
}
if (Meteor.isClient) {
Meteor.subscribe("users");
}
First of all some advice: you can not define a collection twice. If you call new Mongo.Collection("users") a second time you will get an error. Therefore, it should be a global variable an not inside a method.
What I can see in your code is that you are trying to use an array as if it were an object. userRecord.username wont work because userRecord has the value of the fetch() which returns an array.
You could either change your code to userRecord[0].username or loop over the results with forEach like so:
var users = new Mongo.Collection("users");
console.log(users.find());
users.find().forEach(function(singleUser){
console.log(EJSON.stringyfy(singleUser));
}
in order to return the first user, you would be better of using findOne which returns the first object in the result.
I'd like to mock the save() function of a Mongoose model. The function I want to test looks like this in a file called user.js:
var User = import('User.js')
post: function(req, res) {
var user = new User({
password : req.body.password,
email : req.body.email,
});
user.save( function(err) {
if (err) {
....
} else {
....
}
});
I tried to write a test that looks like this in another file called user_spec.js:
var Hander = require('user.js')
it('works properly', function() {
spyOn(User, 'save').andReturn(null)
Handler.post(req, res);
});
but that gives me the error:
save() method does not exist
I've done some more digging and it looks like the User model itself does not have the save() method, an instance does. This would mean I have to mock the constructor of User, but I'm having a lot of trouble with this. Other posts refer to a statement like:
spyOn(window, User)
to fix this, but in NodeJS, global (the window equivalent here), does not have User, since I import is as a variable. Is it possible to mock the constructor to give me something with a mocked save()? I've also taken a look at an npm module called rewire, but I was hoping I could do this without mocking and replacing the entire user module in my handler.
This does not solve the issue of mocking a local variable, but it will solve the issue of unit testing the creation of new documents.
When creating a new document, it is better to use Model.create(). This can be mocked effectively, and it is simply less code. The right way to handle this and test it would be:
var User = import('User.js')
post: function(req, res) {
User.create({
password : req.body.password,
email : req.body.email,
}, function(err) {
if (err) {
....
} else {
....
}
});
});
Corresponding test:
var Hander = require('user.js')
it('works properly', function() {
spyOn(User, 'create').andReturn(null)
Handler.post(req, res);
});
Hopefully this workaround will help other people getting frustrated with jasmine and mongoose unit testing.
You can only swap a function with a spy after the object is created. Hence this will work:
var user = new User(…);
spyOn(user, save).…;
doSomething();
where this will not:
spyOn(User, save).…
doSomething()
Of course you could change the function inside mongoose that creates the save function on the object… but you probably don't want to go there.
In a sane world, you would be able to do this.
spyOn(Model.prototype, 'save')
However, Mongoose tries to overload all their Model functions to work as node.js callbacks and Promises simultaneously. To do this, they manipulate the prototype in a way that is a little hard to predict without reading the actual Model code (https://github.com/Automattic/mongoose/blob/master/lib/model.js).
Here's an example that actually worked for me.
spyOn(Model.prototype, '$__save').and.callFake(function (options, callback) {
callback();
});
For the record, I am using Mongoose with Promises in the application code.
I am new to both node.js and promise style function call. By looking at an denodeify example at http://runnable.com/Ulatc0QnzUgUAAAK/adapting-node-js-with-q-for-promises, I am trying to denodeify the methods of the node.js node-ftp module as following:
var ftp = require('ftp');
var q = require('q');
var ftpClient = new ftp();
ftpClient.on('ready', function() {
var ftpList = q.denodeify(ftpClient.list);
ftpList().then(function(list) {
console.log(list);
}.then(null, function(err) {
console.log(err);
}).done(function() {
ftpClient.end();
});
});
ftpClient.connect();
However, when running that code with node, it shows the error "list error: TypeError: Object # has no method '_pasv'"
I am not sure what's wrong with that piece of code. Does anyone know what's wrong with that? Can you point me some way to debug/troubleshoot the cause of that error message?
Thanks.
When you pass
ftpClient.list
to Q.denodefiy, you are getting the function object, list from the ftpClient object. It will be just a function and the relationship with the parent is lost. This is important because, the bound function list might be dependent on the ftpClient object. So, you must make sure that link is not broken.
Quoting from the Q.denodeify docs,
Note that if you have a method that uses the Node.js callback pattern,
as opposed to just a function, you will need to bind its this value
before passing it to denodeify, like so:
var Kitty = mongoose.model("Kitty");
var findKitties = Q.denodeify(Kitty.find.bind(Kitty));
The better strategy for methods would be to use Q.nbind, as shown below.
So, you can fix your code in two ways,
Using Q.denodeify and Function.prototype.bind, like this
var ftpList = q.denodeify(ftpClient.list.bind(ftpClient));
Using Q.nbind, like this
var ftpList = q.nbind(ftpClient.list, ftpClient);
you need to use q.nbind
q.nbind(ftpClient.list, ftpClient);