Cannot access a collection in Angular-Meteor - javascript

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()) { ... }
});

Related

Accessing Post html from Ghost Blog Custom Handlebars Helper

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.

Calling object functions with variables

I'm building a simple node.js websocket server and I want to be able to send a request from a client to the server and have it just take care of things (nothing that could cause harm). Ideally the client will pass the server an object with 2 variables, one of them for the object and the other for the specific function in that object to call. Something like this:
var callObject = {
'obj': 'testObject',
'func':'testFunc'
}
var testObject = {
func: function(){
alert('it worked');
}
}
// I would expect to be able to call it with sometihng like.
console.log( window[callObject.obj] );
console.log( window[callObject.obj][callObject.func] );
I tried calling it with global (since node.js doesn't uses it instead of a browsers window) but it won't work, it always tells me that it can't find callObject.func of undefined. If I call a console.log on callObject.obj it shows the objects variable, as a string, as expected. If run a console.log on the object itself I get the object back.
I'm guessing this is something rather simple, but my Google-fu has failed me.
My recommendation is to resist that pattern and not have client code pick any function to call. If you are not careful you have built yourself a nice large security hole. Especially if you are considering using eval.
Instead have a more explicit mapping between data sent by the client and server code. (Similar to what routes in express what give you).
You might have something like this
const commands = { doSomething() { ... } );
// Then you should be able to say:
let clientCommand = 'doSomething'; // from client
commands[clientCommand](param);
This should be pretty close to what you want to achieve.
Just make sure doSomething validates any parameters passed in.
For two levels of indirection:
const commandMap = { room: { join() { ...} }, chat: { add() { ... } }};
// note this is ES6 syntax
let clientCmd = 'room';
let clientFn = 'join';
commandMap[clientCmd][clientFn]();
I think you might just have to find the right place to put the command map. Show your web socket handler code.

Meteor Method not defined in Template from schema

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);

Angular Translate async timing issue with $translateProvider.useStaticFilesLoader

I am using the excellent Angular Translate ($translate) directive/service to deal with multiple Locale Languages and since I have multiple locale files I use the convenient $translateProvider.useStaticFilesLoader to load my translation files through a structure of localeAbbr.json, for example en.json, es.json, etc... I built a Plunker to show my open source project and that project uses the locale through Git raw files (pointing to the actual Github repository, meaning not local to the plunker demo). My project is built as a Directive and a Service, I made a small Plunker to show my timing issue with the JSON file loading.
All that to say that it seems $translateProvider.useStaticFilesLoader works asynchronous while I would really need it to be synchronous because by the time the plunker runs, the JSON files are not yet parsed while I already called a $translate.instant() on my messages.
I have a Plunker showing the problem.
And here is part of my quick Service demo:
app.factory('validationService', ['$filter', '$translate', function ($filter, $translate) {
var service = this;
var validationSummary = [];
var errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
//var $translate = $filter('translate');
for(var i=0, ln=errorMessages.length; i < ln; i++) {
validationSummary.push({
field: i,
message: $translate.instant(errorMessages[i])
});
}
// attach public functions
service.getValidationSummary = getValidationSummary;
return service;
// function declaration
function getValidationSummary() {
return validationSummary;
}
}]);
The $translateProvider configuration
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'https://rawgit.com/ghiscoding/angular-validation/master/locales/validation/',
suffix: '.json'
});
// load English ('en') table on startup
$translateProvider.preferredLanguage('en').fallbackLanguage('en');
}]);
Call my Service through the Controller:
app.controller("TestController", function($scope, validationService) {
var vm = this;
vm.displayValidationSummary = true;
vm.validationSummary = validationService.getValidationSummary();
});
and finally the HTML using the controller:
<div class="alert alert-danger alert-dismissable" ng-show="vm.displayValidationSummary">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="displayValidationSummary = false">×</button>
<h4><strong>{{ 'ERRORS' | translate }}!</strong></h4>
<ul>
<li ng-repeat="item in vm.validationSummary">{{item.field }}: {{item.message}}</li>
</ul>
</div>
Since I'm using AngularJS 1.3+, I also found that $translate only gets translated once, so the author suggest to use translateFilter.$stateful = true; and I tried but that doesn't seem to help.
Again here is the Plunker
I have been spending weeks on trying to find and code all kind of solution but I never got it to work and I'm really sad of seeing my raw translation code :(
Please Help!!!
EDIT
I realized that my question was not covering everything related to my problem. On top of the translation delay problem, I also have to pass extra arguments and that is a huge problem passing them to the translation anonymous function. By the time the promise is finished, the state of my arguments have already changed. For example:
$translate(validator.message).then(function(translation) {
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, translation);
// error Display
if(!isValid) {
updateErrorMsg(translation, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
When working with AngularJS, or JavaScript for that matter you really need to embrace the asynchronous paradigm. In order to make dealing with asynchronous code less cumbersome you can employ the use of Promises. Angular gives you a service called $q which does the heavy lifting for you
https://docs.angularjs.org/api/ng/service/$q
getting ones head around Promises can take time but well worth the effort in the long run.
Essentially what you need to do with your validationService is to make use of $translate's promise api which will give you the translation you require based on the supplied key when it is in a position to do so. What this boils down to is that you ask $translate for all of the translationId's you wish to get a translation for and when all have been fetched you populate the validationSummary array with your messages.
app.factory('validationService', ['$q', '$translate', function ($q, $translate) {
var translationsPromises = [],
validationSummary = [],
errorMessages = [
'INVALID_ALPHA',
'INVALID_ALPHA_SPACE',
'INVALID_ALPHA_NUM',
'INVALID_BOOLEAN'
];
angular.forEach(errorMessages, function(val, key) {
translationsPromises.push($translate(val));
});
$q.all(translationsPromises)
.then(function(translations) {
angular.forEach(translations, function(val, key) {
validationSummary.push({
filed: key,
message: val
});
});
})
.catch(function (err) {
console.error('Failed to translate error messages for validation summary', err);
});
// function declaration
function getValidationSummary() {
return validationSummary;
}
return {
getValidationSummary: getValidationSummary
};
}]);
I've forked your plunker and modified it to include the above sample
http://plnkr.co/edit/7DCwvY9jloXwfetKtcDA?p=preview
Another observation is that you are using the translate filter in the HTML. Please be aware that this can prove to be expensive if you have a large DOM as Angular will make the call to translate each key on every digest. An approach to consider would be to provide your vm with a labels object and use the $filter service to populate them upon controller instantiation.
I found out the answer to my problem of passing extra arguments to the anonymous function of the promise is to use Closures, in this way the variables are the same before the promise and inside it too. So I basically have to wrap my $translate call into the closure, something like the following:
(function(formElmObj, isValid, validator) {
$translate(validator.message).then(function(translation) {
message = message.trim();
// only log the invalid message in the $validationSummary
addToValidationSummary(formElmObj, message);
// error Display
if(!isValid) {
updateErrorMsg(message, isValid);
}else if(!!formElmObj && formElmObj.isValid) {
addToValidationSummary(formElmObj, '');
}
}, function(data) {
throw 'Failed to translate' + data;
});
})(formElmObj, isValid, validator);
and now finally, my variables are correct and keep the value at that point in time :)
While it is true that $translateProvider.useStaticFilesLoader does not return a promise, I looked inside the $translate service and found that it provides a handy callback onReady() which does return a promise. This callback is invoked when the $translate service has finished loading the currently selected language, and is useful for making sure that instant translations will work as expected after page initialization:
$translate.onReady(function () {
// perform your instant translations here
var translatedMsg = $translate.instant('INVALID_ALPHA');
});

Restangular Multiple IDs in GET

I'm looking at switching from resource to restangular in my AngularJS app. I like the new model, but I have one problem. I can't figure out how to make this API call:
http://localhost:8000/api/entity/groups/1,2,3,4/events
I have code like this:
var GroupService = ['Restangular', function (Restangular) {
var restAngular = Restangular.withConfig(function (Configurer) {
Configurer.setBaseUrl('/api/entity');
});
return {
getEvents: function (groupIds) {
return restAngular.all('groups', groupsIds).getList('events');
}
}
}];
Is there a way to ask for a set of groups (more than one and not all of them) and then get all events for any of those groups?
Use several method of Restangular
Restangular.several('accounts', 1234, 123, 12345).customGETLIST('events');
P.S.: I think this might be a bug of Restangular that .getList('events') is not working.

Categories

Resources