Using Pug (Jade) with Angular (with CLI)
I love Pug. Pug allow me to write cleaner HTML.
From HTML like this:-
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>my portal</title>
</head>
<body>
<app-root>
<div class="root-spinner">
<img class="loading" src="assets/images/logo.png">
</div>
</app-root>
</body>
</html>
To Pug, so much cleaner.
doctype html
html
head
meta(charset="utf-8")
title my portal
body
app-root
div.root-spinner
img.loading(src="assets/images/logo.png")
However, if you are using Angular (version 2 or 4) & the de-facto Angular-CLI, the pug option is not out of the box, probably yet. There is a discussion here: https://github.com/angular/angular-cli/issues/1886.
How we can use it now?
We can use pug-cli now. Pug CLI allows you to compile pug with command like pug [dir,file]
` and watch the changes.
npm install pug-cli --save-dev
After installation, we create two scripts in package.json
`:
"pug-it": "pug src",
"pug-it:w": "pug src -P -w"
The first command `pug-it` will compile all the .pug files under `src` directory to .html file in same directory.
The second command did exactly the same thing with additional file watching. We use this command during development to watch the file changes and recompile automatically.
Here are some other scripts we have.
"ng": "ng",
"start": "run-p pug-it:w server",
"server": "ng serve --port 4210",
"prebuild": "yarn run pug-it",
"build": "ng build",
Please notes that:-
- When running
npm start
, we will start the dev server and and the pug watch tasks concurrently. - Before we run build, we run pug before that (during
prebuild`
).
Side notes on concurrent tasks
If you want to run multiple tasks concurrently cross platforms (mac, windows, etc), 3rd party packages like concurrently and npm-run-all are helpful. We use npm-run-all
. The run-p
command is provided by the package. You can install it:
npm install npm-run-all --save-dev
However, if all your developers are on Linux or Mac, then you don’t need any 3rd party packages, just replace start
command withnpm run pug-it:w & npm run server
will do.
Git Ignore HTML files
If you are using pug, you might not want to check in the generated HTML files. Exclude them in your git. Add this line to your .gitignore
file.
#pug
/src/**/*.html
Please notes that in your component typescript, you still refer to .html
, there’s no need to change this.
@Component({
selector: 'my-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {}
Woala, everything up and running.
Trade-offs
There are a few tradeoffs using this approach.
1. No auto watching for new files
When adding any new pug file after npm start
, the new file is not watched. You need to restart dev server (stop and rerun npm start
).
2. Fail silently when hitting pug syntax error
Since we start pug watch and dev-server concurrently, when there’s pug syntax error happens during development, you will see errors in terminal, but not on screen or browser console. Please beware of this, sometimes you got syntax error without knowing it, and spend hours to debug other area (you know what I mean).
3. Need to manually create pug file if you use ng generate
By default, angular-cli ng generate
will generate HTML file for component. You need to rename or delete/create the HTML to pug file.
Some captcha when using pug with Angular
Let’s be honest, Angular template syntax looks different from normal HTML. When using it with Pug, you need to surround all attribute with single quote.
my-top-bar(
'[(ngModel)]'="myModel"
'[propA]'="myPropA"
'(actionA)'="handle($event)"
)
This will generate the following HTML.
<my-top-bar
[(ngModel)]="myModel"
[propA]="myPropA"
(actionA)="handle($event)">
</my-top-bar>
If you somehow forgot to put single quote on the attributes, congratulation. No error will be notify in terminal. The HTML generated will be
<my-top-bar>
</my-top-bar>
All fields will be omitted. If you realise about that after an hour of debugging, please don’t cry. Heh.
Please note that
Other alternatives
There are some other alternatives.
- First one, the simplest one, don’t use pug, use HTML.
- Run
ng eject
to eject the angular-cli. You’ll get back all your webpack config that angular-cli generated for you. Then add your pug-loader. - Use other angular boilerplate that you can make changes easier. E.g. Angular Seed.
- Create a pug plugin or angular-cli or wait for one.
- Read the discussion https://github.com/angular/angular-cli/issues/1886, probably you can dig out some gold there.
Conclusion
Our team found the option we chose and the tradeoffs bearable. We’ve use this in our v2 project now upgrade to v4.
Let me know if you have better way of doing this.
That’s it. Hopefully you find this helpful. Happy coding!