While running a website you’ll probably iterate on the design and functionality. Adding new pages is a good start to take a different path from your layout and apply new design ideas.
At Future Studio, we’ve applied a different design to the galaxy map, compared to other pages. Using layouts in handlebars simplifies this process a lot. This tutorial walks you through the hapi setup on how to use different handlebars layouts.
hapi Series Overview
- What You’ll Build
- Prepare Your Project: Stack & Structure
- Environment Variables and Storing Secrets
- Set Up MongoDB and Connect With Mongoose
- Sending Emails in Node.js
- Load the User’s Profile Picture From Gravatar Using Virtuals in Mongoose
- Implement a User Profile Editing Screen
- Generate a Username in Mongoose Middleware
- Displaying Seasons and Episodes for TV Shows with Mongoose Relationship Population
- Implementing Pagination for Movies
- Implement a Watchlist
- Create a Full Text Search with MongoDB
- Create a REST API with JSON Endpoints
- Update Mongoose Models for JSON Responses
- API Pagination for TV Shows
- Customize API Endpoints with Query Parameters
- Always Throw and Handle API Validation Errors
- Advanced API Validation With Custom Errors
- Create an API Documentation with Swagger
- Customize Your Swagger API Documentation URL
- Describe Endpoint Details in Your Swagger API Documentation
- 10 Tips on API Testing With Postman
- JWT Authentication in Swagger API Documentation
- API Versioning with Request Headers
- API Login With Username and Password to Generate a JWT
- JWT Authentication and Private API Endpoints
- Refresh Tokens With JWT Authentication
- Create a JWT Utility
- JWT Refresh Token for Multiple Devices
- Check Refresh Token in Authentication Strategy
- Rate Limit Your Refresh Token API Endpoint
- How to Revoke a JWT
- Invalidate JWTs With Blacklists
- JWT Logout (Part 1/2)
- JWT “Immediate” Logout (Part 2/2)
- A Better Place to Invalidate Tokens
- How to Switch the JWT Signing Algorithm
- Roll Your Own Refresh Token Authentication Scheme
- JWT Claims 101
- Use JWT With Asymmetric Signatures (RS256 & Co.)
- Encrypt the JWT Payload (The Simple Way)
- Increase JWT Security Beyond the Signature
- Unsigned JSON Web Tokens (Unsecured JWS)
- JWK and JWKS Overview
- Provide a JWKS API Endpoint
- Create a JWK from a Shared Secret
- JWT Verification via JWKS API Endpoint
- What is JOSE in JWT
- Encrypt a JWT (the JWE Way)
- Authenticate Encrypted JWTs (JWE)
- Encrypted and Signed JWT (Nested JWT)
- Bringing Back JWT Decoding and Authentication
- Bringing Back JWT Claims in the JWT Payload
- Basic Authentication With Username and Password
- Authentication and Remember Me Using Cookies
- How to Set a Default Authentication Strategy
- Define Multiple Authentication Strategies for a Route
- Restrict User Access With Scopes
- Show „Insufficient Scope“ View for Routes With Restricted Access
- Access Restriction With Dynamic and Advanced Scopes
- hapi - How to Fix „unknown authentication strategy“
- Authenticate with GitHub And Remember the Login
- Authenticate with GitLab And Remember the User
- How to Combine Bell With Another Authentication Strategy
- Custom OAuth Bell Strategy to Connect With any Server
- Redirect to Previous Page After Login
- How to Implement a Complete Sign Up Flow With Email and Password
- How to Implement a Complete Login Flow
- Implement a Password-Reset Flow
- Views in hapi 9 (and above)
- How to Render and Reply Views
- How to Reply and Render Pug Views (Using Pug 2.0)
- How to Create a Dynamic Handlebars Layout Template
- Create and Use Handlebars Partial Views
- Create and Use Custom Handlebars Helpers
- Specify a Different Handlebars Layout for a Specific View
- How to Create Jade-Like Layout Blocks in Handlebars
- Use Vue.js Mustache Tags in Handlebars Templates
- How to Use Multiple Handlebars Layouts
- How to Access and Handle Request Payload
- Access Request Headers
- How to Manage Cookies and HTTP States Across Requests
- Detect and Get the Client IP Address
- How to Upload Files
- Quick Access to Logged In User in Route Handlers
- How to Fix “handler method did not return a value, a promise, or throw an error”
- How to Fix “X must return an error, a takeover response, or a continue signal”
- Query Parameter Validation With Joi
- Path Parameter Validation With Joi
- Request Payload Validation With Joi
- Validate Query and Path Parameters, Payload and Headers All at Once on Your Routes
- Validate Request Headers With Joi
- Reply Custom View for Failed Validations
- Handle Failed Validations and Show Errors Details at Inputs
- How to Fix AssertionError, Cannot validate HEAD or GET requests
View Rendering in hapi
To apply handlebars layouts in hapi, you need to set up view support in your application using the vision
plugin. There’s a dedicated tutorial on how to render and reply views.
If you didn’t prepare your project for views yet, head over to the linked tutorial and move on once you’ve completed the setup.
The Single Layout Problem
While configuring the view support with server.views(config)
in your project, you can specify a layout that will be used to render all your views. The setup in your project might look like this:
server.views({
engines: {
html: require('handlebars')
},
layout: 'my-layout',
path: Path.resolve('path/to/view/template/files')
})
With the layout: 'my-layout'
property, you’re telling vision to use the my-layout
file from within your views folder. The path to your view files is defined within path
.
The problem with this setup is that vision uses my-layout
for all pages that get rendered on server side. The next paragraph shows you how apply a different layout when using reply.view
.
Using Multiple Handlebars Layouts
Let’s say your my-layout
file specifies the layout for your site that is suitable for 80% of your web pages. It imports your header and footer, and keeps the content responsive using a CSS framework’s grid.
Within the rest 20% of pages you want to add a dedicated hero area with a gradient background and highlight a single element.
The requirements for sites with and without the hero area can be processed using two different layouts (with their custom placeholders and imports).
Well, hapi and especially vision
allow you to define a different layout when rendering a view from your route handler:
hapi v17
server.route({
method: 'GET',
path: '/second',
handler: (request, h) => {
return h.view('index', null, { layout: 'another-layout' })
}
})
hapi v16
server.route({
method: 'GET',
path: '/second',
handler: (request, reply) => {
reply.view('index', null, { layout: 'another-layout' })
}
})
The short snippet above illustrates that reply.view
allows three parameters:
- template: the template file name, located within the view’s path
- context data: (optional) used to render data into your view placeholders (e.g.,
{{tag}}
in handlebars) - options: (optional) view configuration object, used to override the server’s view manager configuration for this individual response
Almost all the view configuration options
from vision
are available, except isCached
, partialsPath
, and helpersPath
. Everything else can be overridden for the view rendering process.
The route handler within the code snippet above overrides the layout
property within the view configuration and instead defines another-layout
to be used for this response.
That’s all the magic, awesome!
Outlook
This tutorial showed you how to benefit from multiple handlebars layouts within your hapi app. If you want to use different layouts throughout your website, make use of available view manager options for the view rendering process within route handlers.
Customize the rendering behavior with dedicated options and use the flexibility given by vision and hapi.
We’re happy to hear your thoughts and comments. Please don’t hesitate to use the comments below or find us on Twitter @futurestud_io. Let us know what you think!
Enjoy coding & make it rock!
Additional Resources
- How to Render and Reply Views in hapi
- Future Studio Galaxy Map outlines upcoming content and platform features