How to properly use dustjs & create helpers in sailsjs? - node.js

I'm using sailsjs to build my website but decided not to use its default EJS for templating.
In ./config/views.js, I have set the the following(after npm install dustjs-linkedin & dustjs-helpers):
engine: 'dust',
In my HomeController ./api/controllers/HomeController.js, I need to do the following to get the language text from ./config/locales/en.json:
title: res.i18n('title'),
//or
siteName: res.__('siteName')
In my home template ./views/home/index.dust, I can access those string with:
<h1>{title}</h1>
<p>{siteName}</p>
However, as the number of strings grow, it will be redundant as I have to set each of the variables in controller before I can use it in my template.
So my questions are:
is there a better or correct way to use the text variables in my template from ./config/locales/en.json without having to define each of them in my controller file?
how to access & use the dustjs module in my controller so that..
I can create custom dustjs helpers? (how to create dustjs helpers?)
also, it seems I cannot configure the settings for i18n in ./config/i18n.js (objectNotation: true not working) Is this the correct place to configure i18n?

Its a long ques. I'll answer the part: I can create custom dustjs helpers? (how to create dustjs helpers?)
Ans. Yes you can. Here's how (in sailsjs):
We can create a globally accessible service in api/services/MultiplyBy2.js
MultiplyBy2.js
module.exports = {
myFunc: function (chunk, context, bodies, params) {
var number = params.num;
number = number*2;
return chunk.write(number);
}
};
And then you can use this in a dustjs template like so:
<p>10 * 2 = {#sails.services.multiplyby2.myFunc num=10/}</p>
Note: The file name (MultiplyBy2) will be lowercase. But the function name will be the same as defined (myFunc);
Hope this helps.

I also had this problem. I further dug into this and figured that it's too much hassle to resolve this issue, and that's when I switched to Handlebars on Sails.js.
Handlebars' i18n's syntax in Sails.js is {{__ 'Welcome' }} .

Related

Using i18n + handlebars in an express app, but not to generate output html, how to localize?

I have an Express app in which I want to use handlebars to generate localised templated emails to be sent out. The problem is that handlebars requires a globally registered helper to translate items inside the .hbs files, while I need to use a construct such as app.use(i18n.init) to make sure my __ function translates according to the right locale in the context of the current request. Setting a locale globally will lead to concurrency issues.
The only 'solutions' (which aren't because they don't solve my problem) I found consist of using handlebars middleware to output html using Express, but that is not what I want to do. I want to generate content that's completely independent from what Express sends back to the client.
This is what I am currently doing, which obviously isn't the way to do it
const i18n = require("i18n")
const Handlebars = require('handlebars')
i18n.configure({
directory: './i18n',
defaultLocale: 'en',
objectNotation: true,
syncFiles: true
})
Handlebars.registerHelper('i18n',
function (str) {
if(!str) return str
return i18n.__(str)
}
)
to be used as
<td>
{{i18n "title"}}
</td>
A possible solution is to do the translations in code by calling i18n.__({phrase: "someText", locale: locale}) but I would like to keep this inside the template.
How can I make sure handlebars uses the i18n instance bound to the Express response object?

How to set active links (navbar) in an async ufront app

I try to figure out how to set active links in a navbar or sitebar for an async ufront application.
On the server I can load and parse it dynamically inside the main (top level) controller via an api call like:
#inject public function init(context:HttpContext) {
ufTrace("HomeController::init");
var navStr:String = "";
//getNavbar loads the navbar html snippet and parses the code to set 'active' some tags in relation to the request uri
var navbarSurprise = siteApi.getNavbar(context.request.uri);
navbarSurprise.handle(function (outcome) {
switch (outcome) {
case Success(navbarStr): navStr = navbarStr;
case Failure(err): navStr = "<h1>Could not load navigation: $err</h1>";
}
} );
ViewResult.globalValues["navBar"] = navStr;
}
but that doesn't work on the client for pushstate urls. (navStr would always be empty)
The ViewResult.hx (line:126) doc states:
Helpers (dynamic functions) can be included in your ViewResult also.
Could this be a place to handle that?
But unfortunately I couldn't find any help/examples how to add helper functions to a ViewResult.
I was also thinking about doing it in a custom ViewEngine. But that seems a bit like overcomplicating things.
Any thoughts about that would be appreciated.
Seems your are looking to render the navbar on the server when processing the request.
I did something like that some time ago by using sipple (another templating engine) but you can also use other engine (i think) like haxe template or erazor etc.
This issue sums up how i processed different partials using stipple
Hope it helps.

Routing in locomotive using ejs

I'm trying out node and some frameworks for node atm, specifically locomotive. However, i seem to be stuck on routing using locomotive. A couple questions i can't find the answer to, so here goes:
why does the locomotive out-of-box install use index.html.ejs as a
filename? Why not just index.ejs? What's the benefit?
i'm trying to add a route to a view: searchName.html.ejs which i
added in the views folder. To achieve this i made a toolController
like this:
var locomotive = require('locomotive').Controller,
toolController = new Controller();
toolController.searchName = function() {
this.render();
}
module.exports = toolController;
I also added a route in routes.js like so:
this.match('searchName', 'tool#searchName');
However, that doesn't work (and yet it's what the documentation says ought to work). The result is a 404 error. So how do i make that route work?
Suppose i want to make a route to eg, anExample.html? How do i go
about that? I notice that in the out-of-the-box app from
locomotive, you cannot enter localhost:3000/index.html . Nor even
localhost:3000/index This seems highly impractical to me, as there
are plenty of users who'll add the specific page they want to go to.
So how can i make that work?
PS: I went through all questions regarding this on stackoverflow and searched the web, but i still can't figure this out.enter code here
The benefit is that this naming scheme allows you to specify several different formats for a single route. So you could have search_name.html.ejs and search_name.xml.ejs, then respond with either view depending on what your client is expecting.
There are a couple issues with the example code you posted. You should be seeing a more descriptive error than a 404, so I'm not sure what's happening there, but here are the fixes to your code that work in my environment.
In the controller:
//tool_controller.js
var locomotive = require('locomotive');
var toolController = new locomotive.Controller();
toolController.searchName = function() {
this.render();
};
module.exports = toolController;
In routes.js:
//routes.js
module.exports = function routes()
{
this.match('searchName', 'tool#searchName');
}
Then, you'll need to change the view to this: views/tool/search_name.html.ejs. It's not clear from the documentation, but locomotive automatically lowercases and underscores actions that are camel-cased, like searchName.
Now start the app and browse to http://localhost:3000/searchName
If you just want to serve a static html file, the easiest way is to just drop it in the public folder. This folder is specifically for serving up static content like client-side js, css, etc. And it works just fine for serving static HTML as well.

How do I use connect-assetmanager in my html/ejs template?

I may be completely misunderstanding how to use connect-assetmanager for my node project. But I have set up which js files I want to package up into a 'group' - but how do I refer to this group in my ejs/jade template?
Surely I need to somehow 'render' this package in my template?
I found the solution:
In your server for example, you could set up a dynamic helper like so:
app.dynamicHelpers({
'assetsCacheHashes': function(req, res) {
return assetsManagerMiddleware.cacheHashes;
}
});
where assetsManagerMiddleware represents your assetManager.
Then in your ejs template, you can render the generated asset package like so:
<script src="/js/<%= assetsCacheHashes.js || 0 %>.js"></script>
In your view source - see which path it is choosing and adjust your path accordingly.
For Jade: script(type='text/javascript', src='/the/groups/route')
Your asset groups should have a route specified. That regex is used to determine if a request is asking for the that group. If it matches, it should serve the group.

Passing raw Markdown text to Jade

I'm playing around with my first Node.js Express application, and as every programmer knows, the first thing you should build when testing out a new framework is a blog! Anyway, I'd like to write the articles in Markdown and then render it in the view. I saw that Jade allows for this to be done inside the view itself, using filters, but I can't get that working.
To simplify the situation, here's an example of what I'm talking about.
//app.js
res.render("article", {
md : "Hello World!\n\n*Woo*"
});
//article.jade
section
:markdown
#{md}
But, that outputs this: <section><h1>{md}</h1></section>... it isn't substituting in the variables I've passed to it.
Then I tried this:
//article.jade
section
:markdown
!{md}
And the output is this:
<section><p>Hello World!
*Woo*</p></section>
So, now it's not parsing the markdown!
I have been able to get this to work by parsing the markdown in the app.js file and then passing the HTML to the view to display, but I don't know, that seems a bit messier.
Is there a way to pass variables into Jade filters?
You can do this with a function passed in to jade from node:
var md = require("node-markdown").Markdown;
Then pass it into the view as a local:
res.render('view', { md:md, markdownContent:data });
Then render it in the jade view by calling the function:
!= md(markdownContent)
The node module node-markdown is deprecated. The marked is advanced new version. You can try like this
var md = require('marked');
Inside your router
res.render('template', { md: md });
Inside your jade template
div!= md(note.string)
I don't think jade can do this out of the box. One way to accomplish it that might feel slightly cleaner than pre-rendering the markdown is to create a helper function called markdown that takes a markdown string and returns HTML. Then you could do something like
section
!= markdown(md)
The markdown function should be included in the locals data when you render the jade template and can directly use a markdown library to convert the markdown syntax to HTML.
If you are using Scalate's Jade support you can enter:
section
:&markdown
#{md}
You can also import external files with:
section
:&markdown
#{include("MyFile.md")}

Resources