Rendering menus as Jade view partials in Express - node.js

I'm having trouble conceptualizing how to render menus with active states in an Express app using Jade view partials.
I have something like this:
ul.menu
li
a(href='/some/route').active
li
a(href='/another/route')
What I'd like is for the .active class to be applied to the current route, so my CSS can render an active UI state.
When I wrote in PHP, this would've been accomplished through some complex chain of if/else, statements, but I'm assuming there's a more elegant approach. What am I missing?

My solution is similar to danmactough's, but I think a little tidier.
I use middleware to save the url as a local variable on all requests. Make sure this comes before your routes.
app.use(function(req, res, next) {
res.locals.url = req.url;
next();
});
My Jade template for a menu looks like this
ul
each link in links
li
a(href=link.url, class=link.url === url && 'active')= link.name

Jade is ignorant about the route. I don't think there's any way to avoid the if/else in one form or another.
In Express, just pass the route as a local:
res.render('my_view', {
current: 'current/url'
});
And in the view, have an array of menu links and do something like:
ul.menu
for each item in links
li
if (item.url == current)
a(href=item.url).active
else
a(href=item.url)

Related

Multiple layouts with Handlebars and ExpressJS?

If I'm using Handlebars as my templating engine with Express 4, it seems like there is only the option to specify a single layout template to use for all your views:
app.engine('hbs', expressHbs({extname:'hbs', defaultLayout:'layout.hbs'}));
What if your app needs multiple layouts? What if viewA.hbs uses one layout and viewB.hbs needs a different layout?
As I'm learning nodejs, I'm coming from a PHP Laravel background where the Blade templating engine has you specify which layout to use at the top of each view file. It makes it really simple to switch between layout templates for any given view.
You should be able to pass in the layout from your route/controller when you call the render method.
router.get('/', function(req, res) {
res.render('home', {layout: 'viewBLayout.hbs'});
});
I am pretty sure jade will let you switch layouts from inside the template but I don't know if you can do that with handlebars.
If you use express-hbs, you can specify a layout in the template with a comment like:
{{!< layout}}
Alternatively, you can try exphbs. It also supports layout comments and multiple layouts can be nested. (Disclaimer: I wrote it.)
make sure you first make two files named "main.handlebars" and "backend.handlebars" in /layouts directory:
Try this code for two routes if you want for example
router.get('/', function(req, res) {
res.render('home', {layout: 'main'});
});
router.get('/backend', function(req, res) {
res.render('home', {layout: 'backend'});
});

node, express, jade: How to process form data

Is there any best practise code pattern how to process HTML form data with express and jade templates? I was wondering, if it would make sense to use a PHP like self calling loop of the form template, say in your router script you have two handler for the same route, one for GET and the other for POST requests. Something like:
exports.getHandler = function(req, res){
res.render('/formhandling/', {mode: "form-filling"});
}
exports.postHandler = function(req, res){
res.render('/formhandling/', {mode: "form-processing"});
}
and the jade template might look like
extends layout
block content
h1 #{title}
if mode == "form-processing"
p Form data processed...
else
form(name="", method="post", action="/formhandling/")
...
Does that make any sense or did I get something completely wrong?
I feel like you could just use jQuery to hide the form after the user submitted, instead of rendering the response again. If you want to do any processing of the form on the server side, you can do that in your app.js.
app.get('/', routes.form);
app.post('/', function(res,req){
/* form processing here
you could also do this with an external route */
});
Edit: Also, see my answer on this question

What is the correct way to use express with a second div that needs data

I have the following layout.jade file:
!!! 5
html
head
body
#left
#leftbody
#center
#centerbody
#container!= body
#right
#rightbody
And I have the following route.js file:
app.get('/',
function(req, res) {
Post.find({}).execFind(
function(err, data) {
res.render('post/index', {
posts: data
});
});
});
I'm currently rendering #centerbody based on links in the #leftbody. I'm wondering how I could use express to populate the #rightbody. Currently I'm populating a ul in #rightbody using an ajax call in document.ready but was wondering if there was another way using express.
I would strongly advise you to have a look at Jade includes, or, even better, Jade template inheritance
This would allow you to keep, for all your pages, the common parts of the page in separated files. And allow easier maintenance. This is definitely the thing to do.

Blocks and Extends in Jade layouts with Express not functioning

I have a small express site written for node. I'm using Jade for layouts, and trying to get a handle on the new extends/block bits. When I use the standard layout/child view pattern list here, everything works great:
https://github.com/visionmedia/express/blob/master/examples/jade/views/layout.jade
I want to get fancy and use blocks to stuff custom html into specific parts of the master layout. So I transitioned over to something that looks more like this:
https://github.com/visionmedia/jade/blob/master/examples/extend-layout.jade
https://github.com/visionmedia/jade/blob/master/examples/extend.jade
Using the exact code in the extend-layout example above, my pages will always render the extend-layout.jade part, but never the content inside. Anything inside of a block never seems to get rendered. I've given this a try, but it doesn't help:
https://github.com/visionmedia/jade/issues/377
I'm running node 0.6.2, express 0.2.5, and jade 0.18. Any takers?
The layout config entry is deprecated now anyway with v3 of Express.
You can only use the block/extends style.
So I've figured out what was up. I was manually specifying the layout to use in express - I was setting it to the correct layout, but removing that line solved the problem. Example (for others who run into this):
exports.index = function(req, res){
res.render('index', { title: 'Express' }) // this works!
res.render('index', { title: 'Express', layout: 'layout' }) // this doesn't work
};
I would have expected a different behavior - including an explicit layout like this simply caused the child view to not render within the master layout.

node.js Express - How to get partial views asynchronously

I've got a layout - navigation menu. In express tutorials theres only old-school pages loading. whole old page is thrown away and a new one is downloaded with all layouts,views and partial views. And i want navigation menu to stay. So how can i do that?
If i'm maybe getting smth wrong with this web pages architecture please guide me.
As #drachenstern said, you want to render only partial HTML fragments, not whole documents including the layout. You can tell express to skip the layout using:
res.render('sometemplate', {layout: false});
If you want to look for Ajax requests as distinct from full-page browser loads, use the req.xhr flag as documented here
Thus you might even be able to do
res.render('sometemplate', {layout: !req.xhr});
You can also use res.partial() which is specifically for rendering partials.
Here is a sample of its usage, where 'browse.jade' is name of the template:
exports.browse = function(req, res){
var Contact = mongoose.model('Contact');
Contact.where({}).asc('surname', 'given_name', 'org').run(function(err, results) {
res.partial('browse', {
locals: { data: results }
});
});
};

Resources