Tech

Templating in NestJS Using HandleBars



As an application grows in complexity, so do its needs. At some point, serving static HTML files may hinder progress or hold back the app’s functionality. Instead, you’ll want to serve dynamic content, a task that templating engines like Handlebars make possible.

Handlebars is a minimal, logic-less templating engine for NodeJS. It’s an extension of the mustache template engine. As it’s a logic-less engine, you can use it to strictly separate presentation from underlying code.

Handlebars has great support as a templating engine from the NestJS framework.

What Is a Templating Engine?

A templating engine is a tool that combines HTML tags and a programming language to create an HTML template with minimal code.

The template engine at runtime injects data into the HTML template to render the final view in the browser.

You might find it complicated to set up a templating engine like Handlebars, since it involves many steps. However, the Express server framework that NestJS uses by default has excellent support for Handlebars.

Step 1: Generate a NestJS Application

Run the following command to scaffold a new Nest Application:

nest new <name of your app>

Step 2: Install Handlebars

Run the following command to install Handlebars using the npm package manager:

npm install [email protected]^5.3.0 @types/[email protected]^5.3.0

Step 3: Configure Express Instance

Navigate to your main.ts file and import NestExpressApplication from @nestjs/platform-express.

Assign NestExpressApplication as a generic type to the create method.

Like so:

const app = await NestFactory.create<NestExpressApplication>(AppModule)

Passing NestExpressApplication to the app object gives it access to ExpressJS-exclusive methods. You’ll need those methods to configure specific Handlebars properties.

Step 4: Configure Handlebars

First, you’ll need to specify the locations where your application will find the HTML and other static files (stylesheets, images, etc.). You can store your HTML files in a “views” folder, and other static files in a “public” folder, respectively.

To specify the locations, start by importing join from path. Then, inside the bootstrap function, set the location for the styles.

Like so:

app.useStaticAssets(join(__dirname, '..', 'public'))

The join function takes an arbitrary number of string arguments, joins them, and normalizes the resulting path. __dirname returns the path of the folder where the main.ts file resides.

Next, set the location for your HTML files, like so:

app.setBaseViewsDir(join(__dirname, '..', 'views'));

Next, import Handlebars into your main.ts file:

import * as hbs from 'express-handlebars';

You’ll need the hbs import to configure Handlebars properties like the extension name, etc.

The default file extension name for Handlebars is .handlebars.

This extension name is lengthy, but you can configure it with the app.engine call. app.engine is a native wrapper function around the express.engine method. It takes two arguments: ext and a callback function. See the Express documentation on app.engine to learn more about it.


Call app.engine(), and as a first argument, pass the string ‘hbs’. Then, as a second argument, call the hbs function and pass in a configuration object with a property extname set to ‘hbs’. This setting changes the extension name from .handlebars to .hbs.

app.engine('hbs', hbs({ extname: 'hbs' }));

Finally, set the view engine to Handlebars like so:

app.setViewEngine('hbs');

Step 5: Create Folders

In your project’s root directory, create two new folders. You’ll use the first, public, to store the stylesheets for your application. In views, you’ll store all your HTML files.

This concludes the setting up of your development environment. In the next section, you will have an overview of the Handlebars syntax and its use in a NestJS application.

The Handlebars Syntax

This section will cover the bulk of the handlebars syntax you need to serve your files dynamically.

Helpers

Helpers are functions that transform output, iterate over data, and render conditional output.

Handlebars provides two types of helpers (Block and Built-in) and you can create custom helpers to suit your needs.

You denote a helper by wrapping it in double curly braces. Prefix its name with a hash (#) for an opening helper tag and a forward-slash (/) for a closing tag.

For example:

{{!-- if the value is true, the div will be rendered else, it won't --}}
{{
<div>The value has been rendered</div>
{{/if}}

If you create a custom helper, you must register it in your hbs configuration object in your main.ts file before you can use it in your application.


import { customHelper } from './helpers/hbs.helpers';


app.engine('hbs', hbs({ extname: 'hbs', helpers: { customHelper } }));

Expressions

Expressions are the unit of a handlebars template. Your use of expressions varies depending on the complexity of the task. You can use them alone in a template, pass them as an input into helpers, and more.

Denote expressions with double curly braces, for example:

<h1>{{message}}</h1>

Partials

A partial refers to a piece of HTML pre-saved because it appears across several HTML files. For example, navbars and footers. You can store that content in one file and include it when necessary.

As with your static and HTML files, you’ll also have to set a partials directory in your app.engine configuration object.

Register your partials directory by adding the following code to your configuration object:


partialsDir: join(__dirname, '..', 'views/partials'),

You can access a partial using the partial call syntax. In double curly braces, enter a greater than symbol (>) followed by the name of the partial.

For example:

{{> nameOfPartial}} 

Layouts

A Handlebars layout is an HTML page used to set underlying meta-data or general structure for other HTML pages in the application. It acts as a container with a placeholder which you can inject dynamic data into.

For example:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Templating in NestJS with Handlebars</title>
</head>
<body>
<header>
{{!-- Navbar Partial --}}
{{>navbar}}
</header>
<div>
{{!-- Body Placeholder --}}
{{{body}}}
</div>
{{!-- Footer Partial --}}
{{>footer}}
</body>
</html>

When you run your code, Handlebars takes the contents of the .hbs file you want to render and injects them into the body placeholder. Then it renders the result as the final HTML page.

You’ll need to register your layouts directory in your app.engine configuration object and set a default layout file. A default layout file is the layout file that Handlebars uses if you do not define a specific layout.

Add the following code to your configuration object to declare a default layout and register a layouts directory:

defaultLayout: 'Name of the layout file',
layoutsDir: join(__dirname, '..', 'views/layouts'),

Rendering a .hbs File

In your controller file, import the Res decorator from @nestjs/common and Response from express.

Then in your route handler, pass an argument, res. Assign a type of Response to res and annotate it with the Res decorator. The Res decorator exposes Express’ native response handling methods and disables the NestJS standard approach.

Next, call the render method on res and pass the name of the file you want to render as the first argument. For the second argument, pass an object containing the name of the layout and the substitute value for an expression.

Handlebars will use the default layout set in your app.engine configuration object if you do not provide a layout.

@Get()
getHello(@Res() res: Response){
return res.render('Name of file', { layout: 'name of layout', message: 'Hello World' });
}

Alternatives to Handlebars

Handlebars is an excellent templating tool with many handy features like helpers and layouts. Still, depending on your needs, you could pick an alternative such as EJS (Embedded JavaScript), Pug, or Mustache.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button