is this node.js mustache partials functionality ideal? - node.js

I'm new to node and have been trying to find a way to integrate mustache in a way thats flexible for how I build templates. After trying hogan, mustache-express and the other permutations of mustache, I found the following to make sense to me.
Im not sure if there is a more efficient way of handling this. Basically I like to have control of partial templates so I can load them in module positions on a master template. So this is the basic setup
I have 3 partial templates and one wrapper/master template, the following values are just for example.
./views/wrapper.tpl
<html>
<title>{{ title }}</title>
<body>
<h1>{{ heading }}</h1>
<div>{{> position_1 }}</div>
<div>{{> position_2 }}</div>
<div>{{> position_3 }}</div>
</body>
</html>
./views/module_1.tpl
<h2>{{module_1.title}}</h2>
<p>{{module_1.body}}</p>
./views/module_2.tpl
<h2>{{module_2.title}}</h2>
<p>{{module_2.body}}</p>
./views/module_3.tpl
<h2>{{module_3.title}}</h2>
<p>{{module_3.body}}</p>
Im using Express but removed the default jade render engine. I only included the code that needs to be added to a default express install.
mustache and fs are needed
.
.
.
var mustache = require('mustache');
var fs = require('fs');
.
.
.
app.get('/', function(req, res){
// grab master template
var wrapper = fs.readFileSync('./views/wrapper.tpl', "utf8");
// load data that will be used in template & partials
var data = {
'title': 'dashboard',
'heading': 'welcome to your dashboard',
'module_1':{
'title': 'module 1 title',
'body': 'module 1 body'},
'module_2':{
'title': 'module 2 title',
'body': 'module 2 body'},
'module_3':{
'title': 'module 3 title',
'body': 'module 3 body'}};
// load partial templates
var partials = {'position_1': fs.readFileSync('./views/module_1.tpl', "utf8"),
'position_2': fs.readFileSync('./views/module_2.tpl', "utf8"),
'position_3': fs.readFileSync('./views/module_3.tpl', "utf8")}
// include partials and then replace variables with given data
var html = mustache.to_html(wrapper, data, partials);
// send output to browser
res.send(html);
});
.
.
.
.
This to me makes alot more sense then other examples I have seen, and it works well with mustache. I am able to give the user custom control as to where they want the modules to be positioned. So my questions are
Is there a more efficient way of doing this?
Is there anything wrong with the way Im going about this?
By bypassing Express rendering capabilities what am I missing out?
Why should I use libraries like consolidate and add another layer to express?
Thanks!

If you don't want express rendering, thats fine in my book, I even removed the whole module in some of my lab projects just to get a better understanding of routing in node.
However I'd really advice against using readFileSync since It's a blocking method and will make concurency a problem, always use readFile so you get that async callback.

Related

Changing script files used on the front end of a node backed website

I'm making a solitaire game using NodeJs and Express backend and phaser.io frontend. It will offer different layouts of cards to play.
I have the game working "just fine" alpha state, but with a single layout of cards setup in HTML like:
<script src="js/gameboards/data.js"></script>
<script src="js/tableau.js"></script>
where data.js is the file that describes the card layout, and tableau.js is the game logic. I have several different files in /gameboards and for the testing/building, I just change the filename when I want to change the layout.
data.js is not a JSON file, it's a JS object
let gameboard = {
info: {
title: "Standard",
description: "6 columns of 5 cards"
},
[...]
deal: function() {
for (let i = 0; i < this.vars.allstacks; i++) {
etc...
That contains simple object data as well as object methods that define patterns and repetition (like multiple stacks, pyramids, etc) so it can't really be made into a JSON or other straight data file.
What I want to do is present a list of anchors/links to the player of the layout options. They'll click the link to get sent into /game with the layout they chose.
The method I thought of was to have /index send POST-data containing the layoutname. Then, in /game
router.post('/', function(req, res, next) { ... })
with the HTML template and res.render containing
<script src="js/gameboards/<layoutname>.js"></script>
to call the right file.
Is there a better way of doing this than what I listed above? It seems kludgey to me as if there would be a more 'nodey' and elegant way to do it.
To do it in the way you describe you need to enable a templating engine like handlebars or Pug. For pug I do something like this:
In server:
const cdnAction = process.env.S3_CDN_ACTIVE;
app.route('*')
.get( (req, res) => {
const cdnCSSPath = `https://s3.amazonaws.com/${process.env.S3_BUCKET_CDN}/audience.css.gz`;
res.render('home', { cdnActive, cdnPath, cdnCSSPath });
});
In pug file 'home.pug' rendered above:
head
if cdnActive === 'true'
link(href=cdnCSSPath rel='stylesheet' type='text/css')
else
link(href='/style/embed.css' rel='stylesheet' type='text/css')

handlebars - add content to head of view from partial

I am using express-handlebars in my project and have the following problem:
Question
I want to be able to add <script> oder such tags to my overall views head from a partial that is called inside the view.
Example:
The view
{{#layout/master}}
{{#*inline "head-block"}}
<script src="some/source/of/script">
{{/inline}}
...
{{>myPartial}}
{{/layout/master}}
The view is extending another partial (layouts/master) that I use as a layout. It adds its content to that ones head block through the inline partial notation, which works fine
the Partial "myPartial
<script src="another/script/src/bla"></script>
<h1> HELLO </h1>
Now I would like that particular script tag in there to be added to my views head-block. I tried going via #root notation but can only reference context there. Not change anything.
I know I could use jquery or similar to just add the content by referencing the documents head and such. But I wanted to know if this is possible at all via Handlebars.
I do doubt it is in any way. But if you have any ideas or suggestions, please do send them my way! Many thanks!!!
UPDATE
This wont work if you have more than one thing injected into your layout / view. Since this happens when the browser loads the page, it creates some kind of raceconditions where the helpers has to collect the things that have to be injected into the parent file. If its not quick enough, the DOMTree will be built before the helper resolves. So all in all, this solution is NOT what I hoped for. I will research more and try to find a better one...
Here is how I did it. Thanks to Marcel Wasilewski who commented on the post and pointed me to the right thing!
I used the handlebars-extend-block helper. I did not install the package, as it is not compatible with express-handlebars directly (Disclaimer: There is one package that says it is, but it only threw errors for me)
So I just used his helpers that he defines, copied them from the github (I am of course linking to his repo and crediting him!) like so:
var helpers = function() {
// ALL CREDIT FOR THIS CODE GOES TO:
// https://www.npmjs.com/package/handlebars-extend-block
// https://github.com/defunctzombie/handlebars-extend-block
var blocks = Object.create(null);
return {
extend: function (name,context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this));
},
block: function (name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
}
}
};
module.exports.helpers = helpers;
I then required them into my express handlebars instance like so:
let hbsInstance = exphbs.create({
extname: 'hbs',
helpers: require('../folder/toHelpers/helpersFile').helpers() ,
partialsDir: partialDirs
});
Went into my central layout/master file that`is extended by my view Partial and added this to its <head> section
{{{block 'layout-partial-hook'}}}
(The triple braces are required because the content is HTML. Else handlebars wont recognize that)
Then in the partial itself I added things like so:
{{#extend "layout-partial-hook"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
And that did the trick! Thanks!!!

Gathering document fragments at rendring time using `pug`

I use pug to generate HTML email messages from a template:
doctype html
html
head
title Hello #{name}
body
...
The title is the subject of the email.
Currently, I extract the title text content by parsing the HTML document rendered by pug. But it doesn't seem to be a very efficient way of doing.
Is there some feature or hook available in pug to collect part of the document while rendering it? I considered pug filters, but as far as I understand, those are not suitable since they are triggered at compile time. Not while rendering the document.
I came to a solution using a mixin:
mixin collect(name)
-
// This is just an ugly hack to
// capture the inner block rendered
// text
const savedHtml = pug_html;
pug_html = "";
if (block) block();
const innerHtml = pug_html;
self[name]=innerHtml;
pug_html = savedHtml+innerHtml;
html
head
title
+collect('title')
| Hello #{self.name}
var pug = require("pug");
const compiledFunction = pug.compileFile('template.pug', {debug:true,self:true});
console.log(compiledFunction(out={
name: 'Timothy',
}));
console.log(JSON.stringify(out));
Displaying:
<html><head><title>Hello Timothy</title></head></html>
{"name":"Timothy","title":"Hello Timothy"}
The code of the collect() mixin is not particularly pretty because as far as I know it there is no elegant way to capture the block() output. So I had to tackle into the internal undocumented pug_html variable.
Or is there a cleaner way to achieve that?

Node.js, Express, Jade - Separate layout files

I'm working on some project with Node.js, Express and Jade, where I'd like to seperate layout files. Inside the main file is already separated header, but I don't know how to do this for sublayout where I need to pass data. In this case I need to pass data to widgets for every view on page, but in the route would be too many things to load data into widgets instead of some easy solution which I'm looking for.
I could do this thing on the way which I described above - to load data in view with every request, but this is somehow time & cpu consuming.
Another way I'm thinking of is to create some sublayout for widgets in which I'd load data once and then would be available all the time without calling data from DB in all requests. What's the best way to do that?
I work with mustache but I think you can use a similar strategy that I do.In most of the mustache templates that I use there is a common header and footer section.Along with the scripts and css files.I have created a separate partials file that exports these partials
.For instance my partial file looks like this.
exports.partials = function (isAuthenticated)
{
var menu;
isAuthenticated ?
menu = {
header: '',
footer: ' '
} :
menu = {
header: '',
footer: ''
}
return menu;
};
exports.staticResources = {
bootstrap :'//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css',
fonts : '//netdna.bootstrapcdn.com/font-awesome/3.0/css/font-awesome.css',
jquery : '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'
};
I have another method called generatePartials which as the name suggest generate the partials for my templates
exports.generatePartials = function(isAuthenticated){
var menu = resources.partials(isAuthenticated);
var partials = {
header : menu.header,
footer : menu.footer,
bootstrap : resources.staticResources.bootstrap,
fonts :resources.staticResources.fonts,
jquery :resources.staticResources.jquery,
};
return partials;
};
Now while rendering the template all I have to do is this
app.get('/routeName',function (req, res){
var partials = require('../helpers').generatePartials(req.isAuthenticated());
return res.render('viewName.html', partials);
};
And that's it.

Client side + Server side templating, feels wrong to me, how to optimize?

In the web app I am making, I use the classical Express+Jade to render client pages and also expose some REST API (let's say : "user list API").
These client pages use provided API to retrieve "user list" and display it. To display it, I use the handlebars template library once the data is retrieved.
It seems a bit dirty to me, using two template engines, parsing the code twice, how to make it better ?
Note : I already optimized the thing by sending the initial data within the client page by inserting it a script variable. This variable is then displayed the same way data received by API would be. The API is only used in case of data refresh.
UPDATE : using jade both server and client side is a good idea but how to seperate / specify ? Wich part of the rendered template should be done by when serving the page and what part will be used by the client ?
That's very easy to use Client side + Server side templating.When we are building some web apps,we should use ajax to get some data and use the callback function to deal with it.So we should render these data on the client side.
The question is how to render them on client side?
Now We just need a client side jade.js.
Follow this document : https://github.com/visionmedia/jade#readme
First
git clone https://github.com/visionmedia/jade.git
Second
$ make jade.js ( in fact the project has already compile the file for us )
so we just need to copy this file to the path that we use.
Third
read my demo below :
<script type='text/javascript' language='javascript' src="lib/jquery-1.8.2.min.js"></script>
<script type='text/javascript' language='javascript' src="lib/jade/jade.js"></script>
<script type='template' id='test'>
ul
li hello world
li #{item}
li #{item}
li #{item}
</script>
<script>
var compileText = $("#test").text();
console.log( typeof( compileText ) );
var fn = jade.compile( compileText , { layout : false } );
var out = fn( {item : "this is item " } );
console.log( out );
$("body").append( out );
</script>
Now you can see the output result in the body
hello world
this is item
this is item
this is item
After reading this demo I think that you would know how to seperate jade server side and client side.If you can understand which one compile the jade template,then all the questions are easy.
Maybe you would have another question now.How to write some jade template codes in *.jade?The document also provide us a way to do it.This Tutorial may help you.
index.jade
!!!5
html
head
title hello world
body
ul#list
script#list-template(type='template')
|- for( var i in data )
| li(class='list') \#{ data[i].name }
|- }
index.js
/* you javascript code */
var compileText = $('#list-template').text();
var compile = jade.compile( compileText , { layout : false } );
var data = [{ "name" : "Ben" } , {"name" : "Jack" } , {"name" : "Rose" }];
var outputText = compile( data );
$("#list").append( outputText );
Use http://github.com/flatiron/plates template engine which will work both on the client side and server side.
A few weeks ago I wrote an npm package for Handlebars templates to share them between client and server. It's pretty basic, but it's been working really well for me so far:
https://github.com/jwietelmann/node-handlebars-precompiler
Edit: I'm separately using "hbs" as the package for server-side rendering. The precompiler just delivers precompiled templates to my public javascripts directory whenever I update my hbs views.

Resources