hogan.js with master pages or layouts - node.js

Is it possible in any way to use hogan.js as template engine with layouts something like
"Razor or master pages in .NET"?
I would get a result like this:
layout.hjs:
contains "header" & "footer"
and
index.hjs:
will include layout.hjs and contain only page content.

sure:
layout.hjs:
{{> header}}
{{$content}}
default content
{{/content}}
{{> footer}}
index.hjs:
{{<layout}}
{{$content}}
your content goes here
{{/content}}
{{/layout}}
see the hogan test file for all it can do:
https://github.com/twitter/hogan.js/blob/master/test/index.js
btw. this is Hogan#3.0.0, get it with a git url with mpn

I'm not sure what you mean, "Razor or master pages in .NET"? What are you looking to do, use view partials?
But the basic way of setting up Hogan.js for Express is as follows:
var express = require('express');
var app = express();
app.set('views', __dirname + '/views');
app.set('view engine', 'hjs');
app.use(app.router);
app.use(express.static( __dirname + '/public' ));
app.get('/', function( req, res, next ) {
res.render('index');
});
app.listen(3000);
You will have to npm install express [--save], npm install hjs [--save], depending if it's inside your package.json already or not.
Then you just make a views directory and throw an index.hjs file and you're set.
Let me know what you want to do with your templates and we can work from there.

Related

How do I get HAML layouts working with haml-coffee and Express 4?

I followed "NodeJS Express app generation with CoffeeScript and HAML" and the express-usage docs, but my layout is not displaying.
I can see the rendered index.hamlc OK, but it doesn't contain the layout. How do I get layout to work with HAML and Express 4?
server.js:
const express = require('express'),
partials = require("express-partials"),
...
app.set('views','app/views');
app.engine("hamlc", require("haml-coffee").__express)
app.use(partials())
app.set("view engine", "hamlc")
index.js:
router.get('/', (req, res) => {
res.render('index', {name: "User"})
app/views/layout.hamlc:
!!!
%head
%title Express App
%body
xxx
!= #body
I viewed the source in Chrome using view-source:http://127.0.0.1/ and it only shows the template contents but not the layout.
I moved the line
app.use('/', indexRouter)
below
app.engine("haml", require("haml-coffee").__express)
app.use(partials())
app.set("view engine", "haml")
and it worked. I didn't think the order would matter. (I also renamed the files .haml to match conventions and enable syntax hilighting.)

pug : TypeError: View is not a constructor

I'm trying to make a website using NodeJS with express and pug for templates.
I integrated Pug using the express docs, but I keep getting TypeError: View is not a constructor on the render function. I really looked, but the only people who had this problem got it with other templates, and were not doing it has simple.
So at the end I did it like this :
var express = require('express');
var server = express();
server.set('views', './views');
server.set('view engine', 'pug');
server.get('/', (req, res) => {
res.render('example', {title: "code", message: "message"});
});
require('./settings')(server); console.log('Settings initialized');
server.listen(server.settings.port);
console.log('Server listening on port', server.settings.port);
And in views/ the template looks like :
html
head
title = title
body
h1 = message
In package.json, I imported it like : "pug": "^2.0.0-rc.1"
Thank you for your tips ! And have a nice day.
-- EDIT --
So I made a little experiment by uninstalling pug ... I have the same result, so I guess I didn't install or parametrized well pug.
-- EDIT --
Made some other tests and it turns out without the require it works well. But I don't see why, and I need to import external files.
This could be happen for various reason one of the most frequent is that there is a typo in the way you use pug in your index.js or where the object is instanced.
After made sure to have installed it locally( check your package.json) and to have created a root a folder called "views", and in there same file that you use in your rooter, then to be sure to use the sintax:
server.set('view engine', 'pug');
server.set('views','./views');// default, but if you specify don't make mistake on this
In your case I believe it is the order that caused this error.
Another note, useful is that the pug sintax, is required that "tags" are followed without space by "=", like:
html
head
title= title
body
h1= message
I had the same error because of a typo in views
server.set('view', './views');
server.set('view engine', 'pug');
I fixed it by putting an s to views
server.set('views', './views');
(this discussion assumes app is express: var app = express();)
You're getting this error because you're stepping on app.settings, which contains a view property.
I understand the appeal of wanting to call app.settings, so you can use app.settings.custom instead, and it won't cause a clash:
settings.ts
module.exports = {
port: 3000
}
app.ts
// settings
app.settings.custom = require('./settings')
...
var server = await app.listen(app.settings.custom.port, async () => {
var address = server.address()
console.log(`app.js - server started at ${new Date().toLocaleTimeString()} in ${app.environment} - http://${address.address}:${address.port}`)
})
If you don't like .custom, you can use Object.assign(app.settings, mySettings), just make sure no property values clash.

Using node and express to serve get(/foo/:bar) requests, when res.render(template), all relative links in template are relative to /foo instead of /

The offending route in my node server code is this:
app.get('/s/:searchTerm', function(req, res) {
res.render('index');
});
After this happens, all the relative links in index.jade are relative to "hostname/s/" instead of "hostname/", which is broken.
My node setup looks like this:
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public/'));
app.use(express.favicon(__dirname + '/public/img/favicon.ico'));
app.use(express.json());
app.use(app.router);
All of these routes, except for the /s/:searchTerm route, work. Even the /results route. There, the relative links in partials/result are relative to 'hostname' as I'd expect.
app.get('/', function(req, res) {
res.render('index');
});
app.get('/s/:searchTerm', function(req, res) {
res.render('index');
});
app.get('/results', function(req, res) {
res.render('partials/results');
});
index.jade simply references layout.jade:
extends layout
layout.jade begins like this:
!!! 5
html(lang='en' data-ng-app='FooApp')
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1.0')
meta(name='author', content='foo#gmail.com`')
link(rel='shortcut icon', href='img/favicon.ico')
title FooApp.com â„¢
// Bootstrap core CSS
link(href='//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css', rel='stylesheet')
// Custom css for this template
link(href='css/style.css', rel='stylesheet')
//if lt IE 9
script(src='https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js')
script(src='https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js')
body(data-ng-controller='SearchController'
.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
.container
.navbar-header
button.navbar-toggle(type='button' data-target='.navbar-collapse'
How do I render a partial and keep links in that partial relative to 'hostname' ???
edit: added index.jade and layout.jade code
edit: ah, I see I'm doing something redundant with favicon
The behavior you are seeing is what is expected as hrefs are relative to the page that loads them. Since your /a/:searchTerm route is nested at /a, hrefs to static assets like img/favicon.ico will pick up their relative parent route. If you want the href to look in the root of your public directory for static assets, then prepend a / to the href like so: /img/favicon.ico

express + stylus + jade, nothing gets compiled

I cannot get this simple app.js to work: static files are served but jade and styl files are not compiled.
Here the __dirname ls:
damianomacbook:www damiano$ ls
app.jade app.js app.styl config.xml johnd.jpg
.jade and .styl files are served normally and plain.
Here what happens when curling css and html files (which the middlewares functions are supposed to generate on the fly):
damianomacbook:www damiano$ curl localhost:8080/app.css
curl: (52) Empty reply from server
damianomacbook:www damiano$ curl localhost:8080/app.html
Cannot GET /app.html
What's missing?
Guilty code:
var express = require('express');
var stylus = require('stylus');
var nib = require('nib');
var app = express();
function compile(str, path) {
return
stylus(str)
.set('filename', path)
.use(nib());
}
app.use(express.logger('dev'));
app.set('views', __dirname);
app.set('view engine', 'jade');
app.use(stylus.middleware({
src: __dirname,
compile: compile
}));
app.use(express.static(__dirname));
app.listen(8080);
Your GET /app.html is failing because serving HTML pages is done with the express router, not middleware, and you don't have any routes defined. The static middleware doesn't convert anything (thus the name), so it's not going to serve /app.html unless there's an actual app.html file on disk. To get /app.html working, add:
app.get('/app.html', function (req, res) { res.render('app');});
//or you probably want app.get('/', ...if you want this to be your home page
//you probably also don't want/need ".html" in your URLs as this has fallen out of style
Your stylus problem is the automatic semicolon insertion monster. You must not put the "return" keyword on a line by itself. Your compile function is returning undefined instead of a stylus instance. Keep the compile formatted as it is on the nib documentation and all is well.

How do I use Node and Express with coffeescript and requirejs?

Here's what I want.
A node application using the express webserver
Using coffeescript on the server and more importantly the client
Using require.js on the client (and eventually on the server)
The recommended way I've been able to find of hooking up coffeescript for the client is to use connect-assets. This seems to require using jade helpers to actually compile coffeescript eg.
!=js('monalisa.js')
seems to compile monalisa.coffee and generate the correct <script> tag. Now I want to use require.js and here I stumble. How do I ensure that connect-assets compiles everything correctly without using the jade helpers?
Here's my fairly simple app.js:
require('coffee-script');
var express = require('express')
, http = require('http')
, path = require('path')
, connectAssets = require('connect-assets');
var publicDir = path.join(__dirname, 'public');
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( connectAssets() );
app.use('/public', express.static(publicDir));
app.use(express.logger());
app.use(express.methodOverride());
app.use(app.router);
});
app.configure('development', function(){
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
});
app.get('/', require('./routes').index);
app.get('/monalisa', require('./routes/monalisa').monalisa);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
I've created a package to help solve this problem; it's called connect-assets-jspaths.
From the readme:
Installation
npm install connect-assets-jspaths
Note, there is a dependency on CoffeeScript.
Server Side Usage
assets = require "connect-assets"
jsPaths = require "connect-assets-jspaths"
# Snip ...
app.use assets()
# Exports the global function exportPaths() and jsUrl(); see below in View Helpers.
jsPaths assets
# Optionally, pass a log function to see progress
# jsPaths assets, console.log
Watch changes and re-compile
Now you can pass some additional callbacks in and it will monitor your connect assets directories for changes.
fileChangedCallback = (err, filePath) ->
console.log "File Changed: #{filePath}"
jsPaths assets, console.log, fileChangedCallback, (err, watcher) ->
console.log "Watcher initialized"
NOTE You'll probably want to disable this for production mode.
View Usage
This module exports two global functions exportPaths() and jsUrl().
// Using this in your view
!= exportPaths("jsPaths")
// Turns into this when rendered in production
<script type="text/javascript">
var jsPaths = { "main", "/builtAssets/js/main.13819282742.js" /* snip all the other file paths */ };
</script>
// Using this in your view
- var mainJsPath = jsUrl("/js/main.js")
script(type="text/javascript", data-main="#{mainJsPath}", src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js")
// Turns into this when rendered in production
<script type="text/javascript" data-main="/builtAssets/js/main.13819282742.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js"></script>
Dynamic RequireJS Paths
Now that we have a variable with our requireJS friendly paths in it, we can set those paths in the RequireJS config
# Example main.coffee file in /assets/js folder
requirePaths =
paths:
jquery: "//cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min"
underscore: "//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min"
backbone: "//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min"
text: "/js/lib/text"
handlebars: "/js/lib/handlebars"
if jsPaths
for own key, value of jsPaths
# Fix up the lib references
key = key.slice 4 if key.slice(0, 4) == "lib/"
requirePaths.paths[key] = value
require.config
paths: requirePaths.paths
shim:
jquery:
exports: "$"
underscore:
exports: "_"
backbone:
deps: ["underscore", "jquery"]
exports: "Backbone"
require ['app'], (App) ->
new App().initialize()
Try mimosa, it'll help you with each one of those things out of the box. http://www.mimosajs.com
mimosa new [name] will give you a starter project with all of it.
Sorry for the new answer, but I decided to go make an account. =)
Mimosa will give you a small Express application if you choose Express as part of the mimosa new workflow. And if you choose CoffeeScript it'll give you an Express app in CoffeeScript. And it'll have RequireJS included in the scaffolded application. So you should not need to rewrite anything. You just need to plug your stuff in. If anything the Express app it gives you will serve as an example for you to do it yourself without using Mimosa.

Resources