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.
Related
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 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');
});
I have an angularjs app, which has several controllers with several viewes. When I started coding the app there were few methods(of similar kind), which were used in more than places, initially it looked OK to me to put those methods where ever needed (two or three controllers). But as my application grew I started noticing the code redundancy issue, as the method was required at more places.
Definitely this was my lack of experience which lead to this issue.
Now I decided to remove this redundant code from my app. I found the following options:
Service way: Remove the redundant code and put it in a new service and include the service all the places where I need the functionality.
External JS: Putting the redundant code in an external js file and just call the required method.
Leave the code as it is.
But here I want to mention that the redundant code I am talking is not very tight coupled and can be added and removed any where with a very little effort.
Now, what I want to know is from the above options, which one should I choose? OR are there much better options available for this issue(may be provided by angularJS itself).
Edit Basic examples of code:as I said
$scope.showSomething = function (show, ele) {
//show or hide the ele (element) based on value of show
}
You are using angular so surely you want to make your app structured according to MVC. what kind of MVC pattern your app will follow if your code is put anywhere in a js file like anything. I would recommend putting your code inside a service. that's the angular way of reducing redundancy. Also you have another option to set the function to the $rootscope here
I'm not sure about your case, but I had similar situation: I had a number of functions which provide validation functionality. I created service:
'use strict';
angular.module('myApp')
.factory('UtilService', ['Env', 'IBANChecker', function(Env, IBANChecker) {
var validateCallbacks = {
checkAddress: function (address) {
return address.action != 'delete';
},
validateIBAN: function (iban) {
return !iban || IBANChecker.isValid(iban);
},
.....
validateYCode: function(id) {
return YCodeChecker.isValid(id);
}
};
return {
/**
* Factory with callbacks for 'validate' directive.
* 0 arguments -- get all callbacks, over wise see #validateCallbacks object to get specific callbacks.
* if wrong callback name requested -> it will be ignored
* #returns {object} -- call requested callbacks.
*/
getValidateCallbacks : function() {
if(arguments.length) {
var result = {};
_.each(arguments, function(argument){
if(validateCallbacks[argument]) {
result[argument] = validateCallbacks[argument];
}
});
return result;
} else {
return validateCallbacks;
}
}
};
}]);
And code in controller looks like:
var validateCallbacks = UtilService.getValidateCallbacks('validateText', 'validateNumber');
_.each(validateCallbacks, function(callback, key) {
$scope[key] = callback;
});
I tend to use factories, specifically because factories can depend on other factories and logical modules can be defined. Consider the psuedo code below.
.controller('HisController', function (UtilityService) {
$scope.foo = UtilityService.foo;
})
.controller('HerController', function (UtilityService) {
$scope.foo = UtilityService.foo;
})
.factory('UtilityService', function (SomeOtherService) {
var service = {
foo: foo,
bar: bar,
}
return service
///////
function foo(a) {
return a.substring(0,1);
}
function bar(a) {
return SomeOtherService.doStuff(service.foo(a));
}
})
In my app there are dynamic parts that are loaded from database as string that looks like:
"define(['dependency1', 'dependency2'], function(){"+
" // function body" +
"})"
which is just a simple requireJS module, as a string. I want to lazy load the script above using async require call. So, my main requireJS script looks like:
require(["jquery"], function($){
$(document).ready(function(){
// logic to load specific script from database
var scriptString = functionToLoadTheStringAbove();
// ideally i would like to call it like this
require([scriptString], function(){
// scriptString, dependency1, dependency2 are loaded
}
});
});
How do I load those string in requireJS? I know about text plugin, but it only allow loading from files. I tried eval but it doesn't resolve dependencies correctly.
This is quite late, but I just post my solution here in case anyone needs.
So I ended up asking in requireJS forum and examining the source of text! plugin and json! plugin. The cleanest way to load module from String in RequireJS is by making your own plugin to load the String, and then use onLoad.fromText() that will eval your String and resolve all dependencies.
Example of my plugin (let's call it db! plugin):
define([], function(){
var db = new Database(); // string is loaded from LocalStorage
return {
load: function(name, req, onLoad, reqConfig){
db.get(name, function(err, scriptString){
if (err) onLoad(err);
else onLoad.fromText(scriptString);
});
}
}
});
You can then use the plugin like:
require(["jquery", "db!myScript"], function($, myScript){
// jQuery, myScript and its dependencies are loaded from database
});
Note:
There's no way to require() from String without eval. This is what onLoad.fromText() does internally. Since eval is evil, you should only use it if you know what String you're going to eval(). If you're using it in browser extension, you might want to relax the CSP policy.
To name your String module, you can use explicit naming syntax. This way, your module will always have the same absolute name.
To answer the question a little more directly, create a plugin like so:
define("load-string",[], function(){
var strings=[],
re_package_name = /^string_module_(\d+)$/;
return {
normalize: function(name, _){
if(re_package_name.test(name)){
return name
}
var nml = "string_module_" + (strings.push(name)-1);
return nml;
},
load: function(name, _, onLoad, config){
if(re_package_name.test(name)){
onLoad.fromText(strings[name.match(re_package_name)[1]]);
}else{
onLoad.error("Invalid package name: ",name);
}
}
}
});
and use it like so:
var world_module = "define([],function(){return 'world!'})";
require(["load-string!" + world_module],
function(x){
console.log("Hello "+x);
})
You should be able to do :
require(["jquery"], function($){
$(document).ready(function(){
// logic to load specific script from database
var scriptString = functionToLoadTheStringAbove();
var olddefine = define; // require js define
var runme; // capture function
define = function (args,func){
runme = func;
}
eval(scriptString);
runme(); // run the function in current scope
define = olddefine; // restore requirejs function
// dependency1, dependency2 are loaded
});
});
Homework done:
The Node Beginner Book
How do I get started with Node.js [closed]
Structuring handlers in Node
Backstory: I wanted to try and write my own framework but I'm running into some troubles, most likely due to not understanding it fully.
What I want to achieve is a syntax that looks like this:
var app = require('./app'); //this part is understood and it works in my current code.
app.get('/someUrl', function(){ //do stuff here });
app.post('/someOtherUrl', function(){ //do stuff here });
I know of the Express-framework that has this same syntax but reading their source code still eludes me.
This might be a trivial task to achieve but I simply can't produce it, yet.
Trying to require('./app'); in a file deeper in the application produces a undefined object, so I'm guessing that a server is a singleton object.
So what have I tried?
My current code looks like this, and somehow I feel like this is the way to go, but I can't apparently do it like this.
I'm omitting all the require(); statements to keep it more readable.
server.js:
var app = module.exports = {
preProcess: function onRequest(request, response){
processor.preRequest(request); //this object adds methods on the request object
var path = urllib.parse(request.url).pathname;
router.route(urls, path, request, response);
},
createServer: function() {
console.log("Server start up done.");
return this.server = http.createServer(this.preProcess);
}
};
exports.app = app;
At the time of writing I'm experimenting with extending this object with a get() method.
index.js:
var app = require('./server');
app.createServer().listen('1337');
the router.route() bit basically sends the request onward into the application and inside the router.js-file I do some magic and route the request onward to a function that maps (so far) to the /urlThatWasRequested
This is the behavior I'd like to leave behind.
I know this might be a pretty tall order but all my code is easily discardable and I'm not afraid of rewriting the entire codebase as this is my own project.
I hope this is sufficient in explaining my question otherwise, please say what I should add to make this a bit more clear.
Thanks in advance!
I'm not exactly sure what your question is, but here's some thoughts:
1) You are creating a circular reference here:
var app = module.exports = {
// some other code
}
exports.app = app;
You add app as a property of app. There's no need for the last line.
2) You need to hold handlers in app. You can try something like this:
var app = module.exports = {
handlers : [],
route : function(url, fn) {
this.handlers.push({ url: url, fn: fn });
},
preProcess: function onRequest(request, response){
processor.preRequest(request);
var path = urllib.parse(request.url).pathname;
var l = this.handlers.length, handler;
for (var i = 0; i < l; i++) {
handler = this.handlers[i];
if (handler.url == path)
return handler.fn(request, response);
}
throw new Error('404 - route does not exist!');
},
// some other code
}
Note that you may alter this line: if (handler.url == path) in such a way that handler.url is a regular expression and you test path against it. Of course you may implement .get and .post variants, but from my experience it is easier to check whether a request is GET or POST inside the handler. Now you can use the code above like this:
app.route('/someUrl', function(req, res){ //do stuff here });
The other thing is that the code I've shown you only fires the first handler for a given URL it matches. You would probably want to make more complex routes involving many handlers (i.e. middlewares). The architecture would be a bit different in that case, for example:
preProcess: function onRequest(request, response){
var self = this;
processor.preRequest(request);
var path = urllib.parse(request.url).pathname;
var l = self.handlers.length,
index = 0,
handler;
var call_handler = function() {
var matched_handler;
while (index < l) {
handler = self.handlers[index];
if (handler.url == path) {
matched_handler = handler;
break;
}
index++;
}
if (matched_handler)
matched_handler.fn(request, response, function() {
// Use process.nextTick to make it a bit more scalable.
process.nextTick(call_handler);
});
else
throw new Error('404: no route matching URL!');
};
call_handler();
},
Now inside your route you can use
app.route('/someUrl', function(req, res, next){
//do stuff here
next(); // <--- this will take you to the next handler
});
which will take you to another handler (or throw exception if there are no more handlers).
3) About these words:
Trying to require('./app'); in a file deeper in the application produces a undefined
object, so I'm guessing that a server is a singleton object.
It isn't true. require always returns the reference to the module object. If you see undefined, then you've messed up something else (altered the module itself?).
Final note: I hope it helps a bit. Writing your own framework can be a difficult job, especially since there already are excelent frameworks like Express. Good luck though!
EDIT
Implementing .get and .set methods is actually not difficult. You just need to alter route function like this:
route : function(url, fn, type) {
this.handlers.push({ url: url, fn: fn, type: type });
},
get : function(url, fn) {
this.route(url, fn, 'GET');
},
post : function(url, fn) {
this.route(url, fn, 'POST');
},
and then in routing algorithm you check whether type property is defined. If it is not then use that route (undefined type means: always route). Otherwise additionally check if a request's method matches type. And you're done!