Partials with Node.js + Express + Hogan.js - javascript

I'm developing a site with Node.js + Express and using as view engine Hogan.js.
This is my file app.js:
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path');
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'hjs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(require('less-middleware')({ src: __dirname + '/public' }));
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', routes.index);
app.get('/about', routes.about);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
The file /routes/index.js is:
/*
* GET pages.
*/
exports.index = function(req, res){
res.render(
'index',
{
title: 'Home Page',
author: 'Bruce Wayne'
}
);
};
exports.about = function(req, res){
res.render(
'about',
{
title: 'About Page',
author: 'Bruce Wayne'
}
);
};
In /views folder, there are:
|- part.hjs
|- index.hjs
|- cv.hjs
The file part.hjs is:
<h3>Hello {{ author }}</h3>
The file index.hjs is:
<h1>Title: {{ title }} </h1>
{{> part }}
Welcome to Gotham City.
And the file about.hjs is:
<h1>Title: {{ title }}</h1>
{{> part }}
I'm not Joker.
I've two questions:
How can I use properly the partials in my pages? (this code doesn't work)
Can I use the same "title" for two or more pages without repeat the values assignment in file /routes/index.js?
Best regards, Vi.

I've found a solution for the first question.
First of all, I removed hjs:
npm remove hjs
Then, I installed the package hogan-express:
npm install hogan-express
Furthermore, I edited app.js:
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path');
var app = express();
app.engine('html', require('hogan-express'));
app.enable('view cache');
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(require('less-middleware')({ src: __dirname + '/public' }));
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', routes.index);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
And routes/index.js:
exports.index = function(req, res) {
res.locals = {
title: 'Title',
};
return res.render(
'index',
{
partials:
{
part: 'part',
}
}
);
};
Now, in /views there are index.html, part.html.
The file part.html contains:
<h1>{{ title }}</h1>
The file index.html contains:
{{> part}}
Hello world!
So, It works fine.

At least in Express 4+, partials just work out of the box. You can use express-generator (from npm) with --hogan or -H option.
After doing that, you need to add partials to the render method:
router.get('/', function(req, res, next) {
res.render('index',
{
title: 'My Site',
partials: {header: 'header'}
});
});
Then, in your template use {{ > xxx }}
<body>
{{> header }}
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
</body>
NOTE: this has header.hjs in views

To use partials with express+hogan, just do the following:
app.get('/yourRoute', function(req, res){
res.render('yourPartial', function(err,html){
var partialHTML = html;
res.render('yourMainView', { myPartial: partialHTML }, function(err,html){
res.send(html);
});
});
}
And now, yourMainView.html:
<p>Something Something Something</p>
{{{partialHTML}}}
<p>Bla Bla Bla</p>
Notice the triple '{' instead of double as you usually do! That telling hogan (mustache) to parse this as HTML rather then a string!
That's it.

As for your partials question, if you use consolidate.js you can simply do:
res.render('index', {
partials: {
part : 'path/to/part'
}
});

This is a problem. Partial support is difficult to come by in Express 3.
Your best bet is:
https://github.com/visionmedia/consolidate.js
npm install consolidate
These patches take different approaches to adding partials for Hogan:
https://github.com/visionmedia/consolidate.js/pull/51
https://github.com/visionmedia/consolidate.js/pull/29
Unfortunately, the engine doesn't have a hook for filesystem based partials natively, so I think people are confused about how and where partials should be implemented. I ended up with LinkedIn's Dust.js implementation, since partial support was already there. Master actually has even better support, plus I submitted a patch yesterday for relative paths.
Josh

I would use mmm instead of hjs.
https://github.com/techhead/mmm
Disclaimer: I wrote the package.
Just replace all occurrences of hjs with mmm and partials will start working. There is a lot more information and an example at the link above.
As for your other question, if you want to share properties across multiple views, you have a couple of options.
When you call res.render(name, options), the options will actually be merged onto res.locals and app.locals before being passed to the rendering engine. Therefore to set an app-wide property, you can simply assign it to app.locals.
app.locals.title = "Default Title"; // Sets the default title for the application
This concept really applies to just about any Express 3 View Engine.
However, for mmm specifically, please see the section under Presentation Logic for more ways to bind values to a template or set of templates.

Related

How to make jade loading the css file? (It doesn't work)

Here is my directory
enter image description here
newTab.jade code
doctype html
html
head
title New Tab
link(rel = 'stylesheet', type = 'text/css', href = '/public/index.css')
body
p hello world
index.css code
p{
background-color: black;
color: white;
}
app.js code
const express = require('express');
const app = express();
const port = 3000;
app.locals.pretty = true;
app.listen(port, function(){
console.log("Server Connected port: "+port);
});
app.set('view engine', 'jade');
app.set('views', './views');
app.get('/', function(req, res){
res.render('newTab');
});
jade file can't loading css file.
I tried
href = "/public/index.css"
but it doesn't work too.
Append a middleware to Express to serve static files too.
In your app.js file:
// require path module to join your public folder with __dirname
const path = require('path');
//...
app.get('/', function(req, res){
res.render('newTab');
});
app.use('/', express.static(path.join(__dirname, 'public')));
There are more things express.staticcan do. E.g. setting the maxAge for caching purpose:
// 31557600000ms = 1 year
app.use('/', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
suggestion
try
doctype html
html
head
title New Tab
include index.css
body
p hello world
on your newTab.jade file
and include index.css on views directory

Handlebars/Node/Express Can't load files

So I'm working on developing a Node/Express webapp for basic CRUD operations and I'm having a hard time implementing Handlebars within the project.
When I try to use handlebars none of my stylesheets from my .hbs (previously .html) pages are loading.
Here's the file tree:
Here is the error:
And here is an example of the script import statements from
index.hbs
<!-- Bootstrap -->
<link href="../vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
And Finally here is the server.js file
var express = require('express'),
bodyParser = require('body-parser'),
path = require('path'),
mysql = require('mysql'),
dbconfig = require('./config/database'),
exphbs = require('express-handlebars');
var connection = mysql.createConnection(dbconfig.connection)
connection.query('USE ' + dbconfig.database);
var app = express();
//Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
//Set static path
//app.use(express.static(path.join(__dirname, 'public')));
//app.use(express.static(__dirname + '/public'));
//app.set('views', __dirname + '/views');
//app.use('/views', express.static(path.join(__dirname, '/views')));
app.engine('hbs', exphbs({defaultLayout: false}));
app.set('view engine', 'hbs');
app.set(express.static(__dirname + '/public'));
//app.use('/views/vendors', express.static(path.join(__dirname, '/views/vendors')));
//app.use(express.static(__dirname + '/public/vendors'));
app.get('/', function(req, res) {
res.render('index');
connection.query("SELECT * FROM Student", function(err, rows){
console.log(rows);
});
});
app.listen(80, function() {
console.log('we are live on 80');
});
I tried using various static paths from other things I found on SO but wasn't able to get any of them to work.
Any help would be greatly appreciated!!
Thanks!
Fixed my problem by adding the following line above my app.get('/'....
app.use("/vendors",express.static(__dirname + "/vendors"));
app.use("/build",express.static(__dirname + "/build"));
app.use("/images",express.static(__dirname + "/images"));
app.get('/', function(req, res) {
I think that can be possible that the problem is in the handlebars configuration, please look at a configuration like this:
app.engine('.hbs',exphbs({
defaultLayout:'layouts',
layoutsDir:path.join(app.get('views'),'layouts'),
partialsDir:path.join(app.get('views'),'partials'),
extname:'.hbs',
helpers: helpers
}));
app.set('view engine','hbs');
maybe in yours it would be:
app.engine('hbs', exphbs({defaultLayout: false, extname:'.hbs',}));
if you are usign app.set(express.static(__dirname + '/public'));
try to put in the public folder your styles for example the bootstrap folder, and then all you have to do is call it in this way:
<link href="/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

Express js external javascript file not loading

I use an express app to serve an static pre-compiled jade file. which includes a external javascript file. but its not loaded while the page gets loaded. but i can access the javascript by the express static/public url. Why its not loading on the html?
app.js
var express = require('express'),
path = require('path'),
sass = require('node-sass');
var app = express();
/* default configurations */
app.set('views', __dirname + '/public');
app.engine('html', require('ejs').renderFile);
app.use(express.static(path.join(__dirname, 'public')));
/* routes */
app.get('/', function(req, res) {
res.send('Hello World');
});
app.get('/home', function(req, res) {
res.render('index.html');
});
module.exports = app;
index.html
<body>
<script type="text/javscript" src="/scripts/site.js"></script>
<script type="text/javascript">
window.onload = function() {
Site.View.init();
}
</script>
</body>
site.js
var Site = Site || {};
Site.View = {
init : function() { alert('view'); }
};
When i open the page in browser i get ReferenceError: Site is not defined
Thanks.
Add app.use('/public/js', express.static(path.join(__dirname, '/public/scripts'))); to app.js, in order to indicate the subfolders of the pulic folder;
If it still not works, change src="/scripts/site.js" to src="/public/scripts/site.js";
site.js must be inside public/scripts from your root directory.
I am not sure but both views and express static pages are gone to public directory in your code.
Use default configuration as:
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
You may also add a route for it like this:
app.get('scripts/site.js', function(req, res){
res.send('./scripts/site.js');
}

How to use Underscore templates instead of Jade in Express?

I don't want to use the Jade templating engine that comes by default with Express. I tried to follow this guide but it fails:
http://blog.luksidadi.com/expressjs-underscore-template/
The error in question is:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: callback function required
at Function.engine (/home/me/blog/node_modules/express/lib/application.js:173:38)
at Object.<anonymous> (/home/tk/blog/app.js:28:5)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Array.0 (module.js:470:10)
at EventEmitter._tickCallback (node.js:192:40)
I get this when I try to start the server with:
node app.js
How to solve this?
app.js:
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path');
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
//app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});
// Add these lines to register underscore template
var _ = require('underscore');
app.engine('.html', {
compile: function(str, options){
var compiled = require('underscore').template(str);
return function(locals) {
return compiled(locals);
};
}
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', routes.index);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
routes/index.js:
/*
* GET home page.
*/
exports.index = function(req, res){
res.render('index.html', { title: 'Express' });
};
layout.html:
< html >
< head >
< title ><%=title%>< /title >
< /head >
< body >
<%=body%>
< /body >
< /html >
index.html:
Hello world
Use consolidate.js to convert Underscore's template functions to accept the format Express requires in 3.x (path[, locals], callback).
First, you are calling app.engine with an extension name and an object whereas it takes a function as second parameter (see source documentation).
This function has 3 parameters : the path to the file, options and the callback.
As, written in the documentation, it's advised to use consolidate.js as an helper to use template engines that are not express friendly.
Here a simple integration of consolidate.js quoted from its README and adapted to use underscore:
// assign the swig engine to .html files
app.engine('html', cons.underscore);
// set .html as the default extension
app.set('view engine', 'html');
Also, I don't know how to handle your layout.html with underscore under Express, I don't think it's possible out of the box.
var cons = require('consolidate');
// view engine setup
app.engine('html',cons.underscore);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
in terminal
npm install consolidate --save

ExpressJS - LESS compiling error on my layout in jade

I had set up compiling LESS on server side in Express, and it worked right in jade without putting less in layout.
my terminal:
if(err) throw err;
^
Error: ENOENT, open '/Users/lijung/Documents/Project/clubond/public/stylesheets/left_navigator.less'
app.js:
/**
* Module dependencies.
*/
var express = require('express')
, path = require('path')
, club = require('./routes/club')
, less = require('less')
, fs = require('fs');
var app = module.exports = express.createServer();
// Configuration
app.configure(function(){
var RedisStore = require('connect-redis')(express);
app.use(express.cookieParser());
app.use(express.session({ secret: "william", store: new RedisStore }));
app.use(express.logger());
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.set('view options', { layout: false });
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// Routes
//index-in-layout
app.get('/club/:id', club.chkExist, club.getDataById, site.clubPage);
//compile less
app.get("*.less", function(req, res) {
var path = __dirname + req.url;
fs.readFile(path, "utf8", function(err, data) {
if(err) throw err;
less.render(data, function(err, css) {
if (err) throw err;
res.header("Content-type", "text/css");
res.send(css);
});
});
});
app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
I put my layout in views named index_in_layout:
!!! 5
html
head
title= title
script(src='/javascripts/jquery.min.js')
link(rel="stylesheet", href="/stylesheets/index.css")
link(rel="stylesheet",type='text/css', href="/public/stylesheets/left_navigator.less")
script(src='/javascripts/index_in.js')
block script
body
index.jade:
extends ./index_in_layout
block script
script(src='/javascripts/new_club.js')
script(src='/javascripts/new_bond.js')
script(src='/javascripts/new_event.js')
script(src='/javascripts/popup.js')
script(src='/javascripts/list_clubs.js')
script(src='/javascripts/list_bonds.js')
script(src='/javascripts/list_events.js')
link(rel='stylesheet', type='text/css', href='/public/stylesheets/test.less')
block body
Terminal keeps telling me Error: ENOENT that my left_navigator.less can't open. I put test.less and navigator.less in the same directory, it makes no sense.
LESS on server side driving me crazy. Can someone help me out please. Thanks
Unless I'm missing something, you really don't need to go through these heroics to get less working :-) Generally you just need to add one line to your app.configure call like this:
app.configure(function() {
app.set('views', __dirname + '/views');
...
app.use(express.compiler({ src: __dirname + '/public', enable: ['less'] }));
app.use(connect.static(__dirname + '/public'));
app.use(app.router);
});
If you do it this way, you don't need the special route for *.less files. You just request *.css with the same name in your public folder and it's automatically generated. I use master/child layouts with jade and LESS here if an example would help:
https://github.com/JustinBeckwith/ExpressStarter
Happy Coding!

Categories