Be more productive with Jade

Be more productive with Jade

Jade is like HTML on steroids. Once learned, you’ll never miss it. This isn’t my first post on Jade. I’ve already written an article about it back in 2013. If you didn’t read it yet, take your time, go and read through the introduction.

This post will cover only a few but mighty features Jade is offering to make you a more productive frontend developer.

Installing jade using npm

To get the following samples up and running, you've to install a Jade compiler. The easiest setup can be achieved by using NPM. The upcoming commands show how to create a new directory and install all required packages that you'll use throughout this post.

$ mkdir jade-samples && cd jade-samples
$ npm init --y
$ npm install jade live-server jstransformer-markdown jstransformer-uglify-js jstransformer-typescript --save-dev

Next open the current directory in an editor of your choice and add the change scripts within your package.json file to match the following

scripts: {  
  "build" : "./node_modules/.bin/jade --hierarchy --pretty --out dist .",
  "start": "./node_modules/.bin/live-server dist"
}

You've just created two scripts that can be invoked from the terminal using npm. build starts the Jade compiler with some flags to keep HTML files readable, respect the folder structure and put all compiled files into the dist folder. The start script spins up a small HTTP server which you can use for testing.

Blocks

You may have already used blocks in Jade by using the block keyword. But there is more. You can also decide where your block of code will be injected - immediately before or after the block statement - in the parent Jade file. Let’s create a bunch of files that we need for all the samples on blocks.

$ mkdir blocks
$ touch blocks/layout.jade
$ touch blocks/simple-blocks.jade
$ touch blocks/append-block.jade
$ touch blocks/prepend-block.jade
$ touch blocks/append-prepend.jade

First let’s create a layout. This layout will be used for all samples on the blocks feature.

// layout.jade
doctype html  
html  
    head
        block styles
            link(rel='stylesheet', href='/vendor.css')
    body
        .container
            block content

The template is providing two blocks that can be used within other Jade files to inject some markup. Notice the styles block which is providing some default HTML.

Our first implementation is done by using regular block statements as shown in simple-blocks.jade

extends ./layout.jade

block styles  
    link(rel='stylesheet', href='/portal.css')
block content  
    h1 Welcome to our portal

Compile both files using our build script

$ npm run build

The compiled html in dist/blocks/simple-blocks.html will look like this

<!DOCTYPE html>  
<html>  
  <head>
    <link rel="stylesheet" href="/portal.css">
  </head>
  <body>
    <div class="container">
      <h1>Welcome to our portal</h1>
    </div>
  </body>
</html>  

As you can see vendors.css has been replaced by the content of the block provided in simple-blocks.jade. This is good for some of the situations, but there are many scenarios where you want different behaviour. Adding Stylesheets is just a simple example here. So let's implement our append-block.jade and use the append keyword to get both stylesheet references in the HTML.

//append-block.jade
extends ./layout

append styles  
    link(rel='stylesheet', href='/portal.css')
block content  
    h1 Welcome to our portal

Compile it again using npm run build and you should receive the following HTML in dist/blocks/append-block.html

<!DOCTYPE html>  
<html>  
  <head>
    <link rel="stylesheet" href="/vendor.css">
    <link rel="stylesheet" href="/portal.css">
  </head>
  <body>
    <div class="container">
      <h1>Welcome to our portal</h1>
    </div>
  </body>
</html>  

That's cool, isn't it? Besides append there is also prepend which we will demonstrate in our blocks/prepend-block.jade file.

extends ./layout

prepend styles  
    link(rel='stylesheet', href='/init.css')
block content  
    h1 Welcome to our portal

As you can imagine, the resulting HTML will render init.css before vendor.css you can also combine both append and prepend to get all three stylesheets in the correct order. See blocks/append-prepend-block.jade for this combination

extends ./layout

prepend styles  
    link(rel='stylesheet', href='/init.css')
append styles  
    link(rel='stylesheet', href='/portal.css')
block content  
    h1 Welcome to our portal

It's also important to realise that as soon as you use either append or prepend the block becomes optional and you don't have to specify block styles explicitly.

Filters

Filters are another great feature of Jade. Because no website is entirely built with Markup, you've always to care about other languages. Filters can make this easier and less error proven. Filters allow you to inject any kind of content - with respect to the HTML specs - into your HTML file. In order to get that working you've to use JavaScript Transformers. Tons of those transformers are available on www.npmjs.org. I've chosen three different ones that I want to demonstrate.

Let's start with an easy one, let's start with markdown. To compile markdown directly into your HTML you need jstransformer-markdown that we've already installed during the beginning of the article. But there is no other manual step required here. The Jade compiler uses those transformers automatically.

Create the folder and files we need for our samples.

# move to the project root directory (jade-samples)
$ mkdir filters
$ touch filters/markdown.jade
$ touch filters/uglify-js.jade
$ touch filters/typescript.jade

Our markdown example is straight forward and goes to filters/markdown.jade

doctype html  
head  
    title markdown sample
body  
    :markdown
        # Hello from markdown
        This should render as **nice looking HTML**

Again compile the sources using npm run build and take a look at the generated html in dist/filters/markdown.html, it should look like this

<!DOCTYPE html>  
<head>  
  <title>markdown sample</title>
</head>  
<body><h1>Hello from markdown</h1>

<p>This should render as <strong>nice looking HTML</strong></p>  
</body>  

That's cool!
We got a clean HTML file. But there is more. Let's take a look at jstransformer-uglify-js, it's responsible for taking regular JavaScript and transforming it to a minified version.

Think about that. We're invoking pre-processors as we normally do using things like Gulp or Grunt but without the configuration overhead. Everything works by indenting the sources one level underneath the filter :uglify-js

doctype html  
head  
    title markdown sample
body  
    script
        :uglify-js
            document.addEventListener('DOMContentLoaded', function(){
                console.log('Hello from JavaScript');
            });

becomes after compiling

<!DOCTYPE html>  
<head>  
  <title>markdown sample</title>
</head>  
<body>  
  <script>document.addEventListener("DOMContentLoaded",function(){console.log("Hello from JavaScript")});
  </script>
</body>  

And finally let's take a look at jstransformer-typescript which is also a mighty pre-processor which will call tsc (TypeScript Compiler) to transpile your TypeScript code directly to ES5 code. The filters/typescript.jade first

doctype html  
head  
    title markdown sample
body  
    button#greet Greet!
    script
        :typescript
            class Greeter{
                greet(name: string){
                    alert(`Hello ${name}`);
                }
            }
            document.getElementById('greet').addEventListener('click', ()=>{
                var g= new Greeter();
                g.greet('Jade Developer');
            });

And finally the corresponding HTML from dist/filters/typescript.html

<!DOCTYPE html>  
<head>  
  <title>markdown sample</title>
</head>  
<body>  
  <button id="greet">Greet!</button>
  <script>var Greeter = (function () {
    function Greeter() {
    }
    Greeter.prototype.greet = function (name) {
        alert("Hello " + name);
    };
    return Greeter;
})();
document.getElementById('greet').addEventListener('click', function () {  
    var g = new Greeter();
    g.greet('Jade Developer');
});

  </script>
</body>  

Mixins with Splats

Last but not least, I'd like to show the combination of a mixin with a splat. Splats are well known language constructs from languages like Ruby or CoffeeScript. It allows you to have a flexible method signature. So your method can receive a flexible range of arguments.

Again let's create folder and files first before bringing them to life.

$ mkdir mixin-with-splats
$ touch mixin-with-splats/sample.jade

The implementation is quiet simple. Look at these few lines of Jade, demonstrating how to define and use such a mixin-splat combination.

mixin nav-list(id, ...items)  
  ul.navigation(id=id)
    each item in items
      li.nav-item= item


+nav-list('main-navigation', 'Home', 'Articles', 'Videos', 'Podcasts', 'Forum')

Once compiled, you will receive the following HTML.

<ul id="main-navigation" class="navigation">  
  <li class="nav-item">Home</li>
  <li class="nav-item">Articles</li>
  <li class="nav-item">Videos</li>
  <li class="nav-item">Podcasts</li>
  <li class="nav-item">Forum</li>
</ul>  

The cool thing here is, it doesn't matter how many arguments you pass to the mixin. It's totally up to the scenario.

Summary

As you can see, Jade is a cool and powerful language that will make you more productive when writing markup. It's worth looking into it and learning those language features. By combining all those simple things you could easily built huge projects in almost no time.

All code written here is also available on Github.

I hope you enjoyed this tutorial! It would be great if you leave a comment and share your opinion about Jade or perhaps your experience gathered while using Jade in the wild.

Comments

comments powered by Disqus