Elvenware

NodeJade

Welcomes to NodeJade

Jade

NOTE: Apparently Jade is now called pug, but I'm not ready to accept that yet. Somethings take time...

Jade is a template library like Handlebars. It also is a bit like Markdown, in that it provides a shorthand syntax for composing HTML pages.

Here is an example of how to layout a Jade template:

doctype 5
html
    head
        title= title
        link(rel='stylesheet', href='/stylesheets/style.css')
    body
        nav
            ul
                li
                    a(href="/") page01
                li
                    a(href="/page02") page02
                li
                    a(href="/page03") page03
        h1 #{title}
        block content

When working with jade, indentation is crucial. You should indent with either tabs or spaces, but not both. You frequently need to open your file in an editor that allows you to visualize your white space. Then you should count your space, and make sure it follows an exact pattern. Suppose, for instance, that you are uses tabs for your indentation. In the pseudo code shown below, each hyphen (dash -) represents one tab.

Example 1 is like what's shown above:

doctype
html
- head
- - title
- - link
- body
- - nav
- - - ul
- - - - li
- - - - li
- - - - li
- - h1
- - block content

Example 2 is a common pattern in bootstrap files:

block content
- container
- - header
- - navbar
- - - nav
- - - - ul
- - - - - li
- - - - - li
- - - - - h3
- - - h1
- - - p
- - - h1
- - - div
- - - footer
- - - - p

When looking at the pseudo code above, the point is that you can explicitly count how many tabs appear before each HTML element. You want to achieve that level of specificity when working with Jade.

Rendering Jade/Pug

Jade/Pug allows us to "pass in" variables (locals) that can be used on the page. This is known as templating.

When Jade is being translated to HTML, the renderer looks at the locals that are passed to it and substitutes these variables for markers on the page.

For instance, in this well-known chunk of code, where the locals have a property called title:

router.get('/', routeParamMiddleware, function(req, res, next) {
    'use strict';
    res.render('index', {
        title: 'Week09-SessionBasics'
    });
});

We can then reference this in our jade like this:

p Welcome to #{title}
For more details on this process, see the overview here:

https://pugjs.org/api/getting-started.html (Links to an external site.)

Even if all this is clear, it still may not be obvious that the locals can take a complex object like user, and that we can reference the fields of that object in our Jade:

response.render('account', {
    title: 'Google Account',
    user: request.user
});

And in the jade:

p DisplayName: #{user.displayName}

Input Controls

Declare a text Input:

input#dirToWalk(type='text', name='dirToWalk')

Put text in it with jQuery:

$('#dirToWalk').val('/home/charlie/Documents/AllTest');

Editors

HINT: Here are some ways to turn on visual whitespace in various editors:

Most programming editors have options like these.

Loading Jade

While reading this section, you might want to also run the accompanying example program, found in the JadeRoutes program from JsObjects.

In the next few paragraphs I describe how Jade templates get loaded in to our web applications. Much of the code I describe is autogenerated for you when you first create the express application. However, some of the code you have to write yourself. Of course, whether the code is autogenerated, or created automatically, you should be sure that you understand all the pieces and what they do.

In our app.js file we link in the views directory and tell express to use Jade. Note that their other ways for express to generate or render HTML files. But we are asking it to render HTML from Jade templates. We do so with code like this:

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

Once again, this code is found in app.js. It is frequently, perhaps even usually, auto-generated by the express-generator. But you should understand what it does.

After processing the above code, express knows to look for Jade files in the views directory. It also knows to render views using the Jade engine.

Our next chore is to tell express where to send requests sent from the browser. The user typically uses HTTP to compose URLs which are sent back to our express server. Depending on the shape of the URL, the express app can handle the request in any number of ways. The following code in app.js tells express where to send requests with a particular type of URL. This is known as routing:

// Link in the index.js file from the routes directory
var routes = require('./routes/index');

// Tell the program to send routes of a certain type to **routes/index.js**.
app.use('/', routes.index);

The above code tells express the following:

In fact, with the code shown above, most requests from the server will end up going to routes/index.js. However, we shall see that it is possible to tell the server to route requests of a certain type to another file. For instance, requests to http://www.example.com/foo could be sent to routes/foo.js.

Render Examples

When the browser sends a request for an HTML page, express can send the request to routes/index.js). Routes found in that file detail how our Jade templates should be rendered.

Here is an example routes/index.js. Study it with care:

var express = require('express');
var router = express.Router();

/* ONE: GET home page. */
router.get('/', function(request, response, next) {
    'use strict';
    response.render('index', {
        author: 'Charlie Calvert',
        title: 'Solar Explorer'
    });
});

/* TWO: Energy Types Jade files routed here. The jade is in the energy-types directory. */
router.get('/energy-types/:id', function(request, response) {
    'use strict';
    response.render('energy-types/' + request.params.id, {
        title: 'Energy Types'
    });
});

/* THREE: Renewable Jade files routed here. The jade is in the renewables directory */
router.get('/renewables/:id', function(request, response, next) {
    'use strict';
    response.render('renewables/' + request.params.id, {
        title: 'Renewable Types'
    });
});

/* FOUR: Other jade files routed here. The jade is in the views directory. */
router.get('/:id', function(request, response, next) {
    'use strict';
    response.render(request.params.id, {
        title: 'Random types'
    });
});

module.exports = router;

Once again, I ask the you study this example carefully. All the routes in this file translate jade into HTML and send the result to the server using HTTP. I have labeled each method as ONE, TWO, THREE and FOUR.

NOTE: We don't explicitly ask for HTML even though it is HTML that will be sent back to the brower. In other words, instead of writing this: http://example.com/about.html, we write this: http://example.com/about. It is a simple matter to teach express to respond properly to either request, but I find it simpler to dismiss with the HTML extension. But this is a matter of taste, and you should feel free to do what seems best to you.

Now let's consider the case where we want requests for anything have to do with renewables to be sent not to routes/index.js but to routes/renewables.js. We do this in large programs where we don't want to overload index.js with too many methods.

Suppose you create a file called routes/renewables.js. To set it up, do this in app.js:

var renewables = require('./routes/renewables');
var routes = require('./routes/index');

// It's usually safest to insert the renewables middleware
// before the middleware found in **routes/index.js**
app.use('/renewables/', renewables);
app.use('/', routes);

In routes/renewables.js, add, for now, just one route:

var express = require('express');
var router = express.Router();

router.get('/:id', function(request, response) {
    'use strict';
    response.render('renewables/' + request.params.id, {
        title: 'ElfComponent'
    });
});

module.exports = router;

Now consider where our different URLs lead:

Notice that the third request routes URLs with http://example.com/renewables/ in them to the routes found in our routes/renewables.js file.

The next logical step would be to create routes/energy-types.js. To do so, just follow the template established by the example shown above for renewables.

Finally, we can, of course, define other routes in these files that have nothing to do with Jade. For instance, we might define a route in renewables.js that loads a JSON file:

var express = require('express');
var router = express.Router();

/* ONE */
router.get('/getRenewables', function(request, response) {
    'use strict';
    fs.readFile('data/renewables.json', 'utf8', function(err, data) {
        if (err) {
            response.status(404).send(err);
        } else {
            var jsonAsObject = JSON.parse(data);
            response.send({ renewables: jsonAsObject });
        }
    });
});

/* TWO */
router.get('/:id', function(request, response) {
    'use strict';
    response.render('renewables/' + request.params.id, {
        title: 'ElfComponent'
    });
});

module.exports = router;

Looking at the above code, it should be obvious that route ONE has nothing to do with Jade. It is about loading JSON. Route TWO, on the other hand, has to be about Jade, since it calls render. As explained above, we have configured our express program to render Jade as HTML and send it back to the browser via HTTP.

Some resources:

Use Unique Names

Don't define the same name for different tasks. If you have a javascript file called /public/javascript/renewables.js, consider avoiding creating entities such as:

Also consider avoiding creating routes in routes/renewables.js that look like this:

router.post('/renewables', function(request, response) { ... })

It is not necessarily an error to do this, but it can cause confusion. Consider instead:

This may not be an idea solution, but it can help you sort out your code. If you don't like this kind of solution, then just be careful when loading javascript, jade and calling routes.

Jade Indent

When thinking about Jade, indentation is crucial. Suppose I write this:

div
p

That creates two separate, unrelated elements, a div and paragraph.

<div></div>
<p></p>

Suppose I write this:

div
   p

Now the paragraph is part of the div because it is indented to the right beneath it.

<div>
   <p></p>
</div>

Suppose you create a div and make it your controller:

div(ngController="MyController")
p {{firstName}}

The above code probably won't work because the paragraph is not part of the controller and hence {{firstName}} is out of scope. To fix it, do this:

div(ngController="MyController")
   p {{firstName}}

Now the paragraph is part of the controller.

When trying to see exactly what is going on with the spacing in a Jade file, considering opening it in geany and turning on View | Editor | Show White Space. Or open it in NotePad++ and choose View | Show Symbol | Show White Space and Tab.

In WebStorm, you can also turn on visible white space, I believe it is: File | Settings | Editor | General | Appearance | White Space.