Can you imagine the combination of Angular and Pug (previously known as Jade) is super awesome and timesaving? If not, read the following lines and reconsider!

When building SPAs using Angular or AngularJS, you use custom HTML markup extensions (aka directives) to instrument Angular how to render your SPA or how the different building blocks of your app should correspond to user interaction. Using those directives is smooth and well known in the Angular Community.

However, I found myself repeating almost the same markup multiple times while building different prototypes on Angular. To get rid of those duplications, *ngFor may be a solution, but concerning performance on mobile devices, you should avoid using those structural directives for such basic things.

At this point, Pug enters the stage. Pug offers stunning but simple mechanisms to make you even more productive. You could easily build compassable mixins, derived templates and other cool things. Besides all the advantages like maintainability, readability or the time you save, it’s also fun to use it!

If you have not used Pug before, see my other posts on Pug here and here

Fasten your seatbelt and prepare for some extra time that you can spend with your friends and family instead of waisting this time with tons of markup code.

Get started — baby steps

FontAwesome is cool, it takes away much pain, like looking for the right images, integrating those in the build process, creating image-maps to reduce HTTP requests… you see, it’s cool!

But it’s frustrating to write <i class="fa fa-foo"></i> for the 3242th time. Let’s build a mixin to get rid of that.

// mixins/base.pug
mixin icn(name,x)
  if(x)
    i(class='fa fa-#{name} fa-#{x}x')
  else
    i(class='fa fa-#{name}')

Using it is simple, include the mixin file and call the mixin using +mixin-name(parameters)

include mixins/base.pug
+icn('globe')
+icn('heart',5)
+icn('github')

This will generate

<i class="fa fa-globe"></i>
<i class="fa fa-heart fa-5x"></i>
<i class="fa fa-github"></i>

Creating Forms

Let’s start with forms. Building forms (as soon as designing those in CSS is done) is a stupid task. It’s all about writing the same input node with tons of attributes multiple times. Here a short mixin for bootstrap based input nodes.

// mixins/forms.pug
mixin input(slug, title, type, placeholder, model)
    .form-group
        label.col-sm-2.control-label(for='#{slug}')= title
        .col-sm-7
            input.input-sm.form-control(id='#{slug}', type='#{type}',[(ngModel)]="#{model}", placeholder='#{placeholder}')

Usage is pretty the same as you’ve learned in the previous sample.

// index.pug
include mixins/forms.pug
+input('firstName', 'First Name', 'text', 'John', 'customer.name')
+input('email', 'Email', 'email', '[email protected]', 'customer.email')

This will render

<div class="form-group">
  <label for="firstName" class="col-sm-2 control-label">First Name</label>
  <div class="col-sm-7">
    <input id="firstName" type="text" [(ngModel)]="customer.name" placeholder="John" class="input-sm form-control"/>
  </div>
</div>
<div class="form-group">
  <label for="email" class="col-sm-2 control-label">Email</label>
  <div class="col-sm-7">
    <input id="email" type="email" [(ngModel)]="customer.email" placeholder="[email protected]" class="input-sm form-control"/>
  </div>
</div>

The interesting part here is all those Angular related directives. See [(ngModel)] for example. Because Pug compiles to HTML during build time, we can pass around the actual bindings as strings and use Pug String interpolation to produce the final, Angular conform Markup.

Combine mixin files

So far we’ve created a icn and a input mixin, both are located in different files mixins/base.pug and mixins/forms.pug, let’s add mixins/index.pug to have a single file, containing all mixins.

// mixins/index.pug
include ./base.pug
include ./forms.pug

Now you can refactor your root pug file to just include mixins/index.jade, so you don’t have to specify each mixin file.

More complex input directives

The amount of HTML compared to the few lines of Pug is immersive. And the difference will grow even more if you create more powerful mixins. Let’s add some nice icons to our input form. The sample shows the input we created a few seconds ago, it should still render without any image. So change the froms mixin to accept an additional icon argument:

// mixins/forms.pug
mixin input(slug, title, type, placeholder, model, icon)
  .form-group
    label.col-sm-2.control-label(for='#{slug}')= title
      .col-sm-7
        if(icon)
          .input-group
            span.input-group-addon
              +icn(icon)
        input.input-sm.form-control(id='#{slug}', type='#{type}', [(ngModel)]="#{model}", placeholder='#{placeholder}')

The indentation is essential here. The ending input node is exactly on the same level as the if statement. So it gets rendered no matter if there is an icon or not. Another important piece is the usage of +icn(icon).

Yes, you’re right were starting to nest multiple mixins. Imagine that power? Using this mixin is again simple. Change the index.jade to match the following.

// index.pug
include mixins/index.pug
+input('firstName', 'First Name', 'text', 'John', 'customer.name')
+input('email', 'Email', 'email', '[email protected]', 'customer.email', 'envelope-o')

Our firstName input remains without an icon. On the other-side will you find a nice looking envelope in front of the email input field. Once again, see the generated markup.

<div class="form-group">
  <label for="firstName" class="col-sm-2 control-label">First Name</label>
  <div class="col-sm-7">
    <input id="firstName" type="text" [(ngModel)]="customer.name" placeholder="John" class="input-sm form-control"/>
  </div>
</div>
<div class="form-group">
  <label for="email" class="col-sm-2 control-label">Email</label>
  <div class="col-sm-7">
    <div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope-o"></i></span></div>
    <input id="email" type="email" [(ngModel)]="customer.email" placeholder="[email protected]" class="input-sm form-control"/>
  </div>
</div>

Integration with Angular

First, let’s take a look at a pure component responsible for rendering an edit form to allow users to change customer’s data.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { CustomerService } from '../../services/customer.service';
import { Customer } from '../../models/customer';
@Component({
    templateUrl: 'detail.html',
    directives: [ROUTER_DIRECTIVES]
})
export class CustomerDetailComponent implements OnInit {

    private customer: Customer;

    constructor(
        private _customerService: CustomerService,
        private _router: Router,
        private _route: ActivatedRoute
        ) {
            this.customer = new Customer();
    }

    public ngOnInit() {
        let id = null;
        this._route.params.forEach((params: Params) => {
          id = +params['id'];
        });
        this._customerService.getById(id)
          .subscribe(c => this.customer = c);
    }

    public save(){
        this._customerService.update(this.customer)
            .subscribe(data=> this._router.navigate(['/customers/list']));
    }
}

This is a regular Angular component. There is no need for changing something here. The templateUrl remains ...detail.html.

You may ask where all the magic happens. Well, it’s inside of a gulp task.

If you’ve never used gulp or need a refreshment, check out my series of posts on Gulp, it is worth reading it!

Once Gulp.js is up and running in your Angular project, there is only a single new dependency you’ve to install to have things you need.

npm install gulp-jade --save-dev

With respect to the common Angular project structure and systemJS, you can build HTML templates to the correct destination by using the following gulp task:

gulp.task('private:build-ng2-templates', function(done){
    return gulp.src('src/templates/**/*.jade')
        .pipe(jade())
        .pipe(gulp.dest('dist/templates'));
});

Hook up this task to the sequence of tasks being executed during a regular build, and you’re done.

Summary

I know many people bashing about Pug. However, I don’t get why. (Most of the time those people were also bashing my beloved CoffeeScript in the past :D) However, in these days we’re using TypeScript to make JavaScript work, Less or Sass to make CSS easier and less painful. So why not using Pug to be more productive in markup?

What’s your opinion? Have you tried it with Angular? Do you have the same impressions I’ve shared here? Alternatively, may it adding too much complexity to your projects? Leave a comment.