hapi — Create and Use Handlebars Partial Views

Within the previous post about hapi and handlebars, we show you how to create a dynamic layout template. Actually, this was just the surface of the functionality hapi provides with its handlebars integration. The view configuration for handlebars with hapi allows multiple options. This article shows you how to use the partialsPath option to add partial layouts into your app.

hapi Series Overview

File Structure

We use the same code structure as we did within the previous post about the dynamic handlebars layout. This keeps us within the same context and allows references between the posts. The views folder contains any view files ending on .html even though the files are located within a subdirectory. Besides the views, we have a single file server.js which describes the minimum functionality required to make this example work properly.

The following overview outlines the the file structure:

views/  
    helpers/
    partials/
        header.html
        footer.html
    layout/
        default.html
    index.html
server.js  

The default layout is located within the layout folder. Our partial views have their own directory called partials. The index.html is a normal view showing the user some funky text :)

Hapi Server View Configuration

As with dynamic layouts, we use the same server code for this example about partial views with hapi. The interesting part is the view configuration for the server. This time, we define the partialsPath property to let hapi know where to find partial views.

const Hapi = require('hapi')

// Create hapi server instance
const server = new Hapi.Server({  
    host: 'localhost',
    port: 3000
})


async function liftOff() {  
  await server.register({
    plugin: require('vision')  // add template rendering support in hapi
  })

  server.views({
    engines: {
        html: require('handlebars')
    },
    path: 'views',
    layoutPath: 'views/layout',
    layout: 'default',
    partialsPath: 'views/partials'
    //helpersPath: 'views/helpers',
  });

  // create your routes
  server.route({
    method: 'GET',
    path: '/',
    handler: (request, h) => {
      // Render the view with the custom greeting
      const data = {
        title: 'This is Index!',
        message: 'Hello, World. You crazy handlebars layout'
      }

      return h.view('index', data);
    }
  })

  try {
    await server.start()
    // Log to the console the host and port info
    console.log('Server started at: ' + server.info.uri);
  } catch (err) {
    console.log(err)
    process.exit(1)
  }
}

liftOff()  

The partialsPath property expects the root path to your partial layout files. Keep an eye on your path definition. Within this example, we use relative paths. It’s better to use the absolute path. Node provides different options like __dirname or the path module. Just make sure the server is able to find the directories, even though it’s not started from within the directory.

View Templates

To provide a comprehensive overview, we show the code for every layout template from the file structure. Additionally, we combine the default and partial layout files to “import” the contents from partial views into the defaut layout.

Default Layout Template

We defined this default layout template as the way to go for every view when rendering a web view with hapi. Hapi automatically uses this layout and includes the defined subview into it. The most important parts for this guide are the {{> header}} and {{> footer}} blocks. Both blocks are used to include the contents from each partial view into the default layout.

views/layout/default.html

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Your Title</title>
    <meta name="HandheldFriendly" content="True" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <!-- import CSS or other 3rd pary libs -->
</head>  
<body>

    <div class="container">

        {{! Import the contents of header.html from partials}}
        {{> header}}

        {{! Every view gets inserted here }}
        {{{content}}}

    </div>

    {{! Import the contents of footer.html from partials}}
    {{> footer}}

</body>  
</html>  

We’ll provide more information about the partial view import within the Partial Views section.

Index View Template

The index.html template is just a simple view and comprises placeholders for {{title}} and {{message}}. Our previous post explains the index view in more detail.

<h1>{{title}}</h1>  
<p>{{message}}</p>  

Partial Views

Handlebars allows the definition of reusable views as shared templates. You can import partials into your view with handlebars {{> partialPath}} arrow bracket operator. The arrow bracket defines an import of a partial view and expects the path to the view file as parameter.

The default layout above imports two partial views: header and footer. You don’t need to specify the file ending when importing views into your layout. The following snippets show exemplary code for each template which gets imported while rendering the default layout.

views/partials/header.html

<header class="main-header">  
    <nav class="navbar navbar-default">
        <div class="container">
            <div class="collapse navbar-collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/home">Home</a></li>
                    <li><a href="/blog">Blog</a></li>
                    <li><a href="/about">About</a></li>
                </ul>
            </div>
        </div>
    </nav>
</header>  

The header layout is just for illustration purposes. We took a basic Bootstrap navigation to include into the default view.

views/partials/footer.html

{{! The main JavaScript files }}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">  
</script>

{{! Whatever you want to put into the footer :)}}

The footer template is the single jquery import. Expand the template as you wish and use more complex separation of layout and additional importS of javascript files.

Result

The resulting HTML template of the index page will look like this:

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Your Title</title>
    <meta name="HandheldFriendly" content="True" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>  
<body>

    <div class="container">

        <header class="main-header">
            <nav class="navbar navbar-default">
                <div class="container">
                    <div class="collapse navbar-collapse">
                        <ul class="nav navbar-nav">
                            <li><a href="/home">Home</a></li>
                            <li><a href="/blog">Blog</a></li>
                            <li><a href="/about">About</a></li>
                        </ul>
                    </div>
                </div>
            </nav>
        </header>

        <h1>This is Index!</h1>
        <p>Hello, World. You crazy handlebars layout</p>

    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
    </script>

</body>  
</html>  

The contents of header.html and footer.html partial views are imported into the default layout file. Additionally, the placeholders within the index.html are replaced with passed data from hapi route definition. Further, the rendered layout from index.html file replaces the {{{content}}} placeholder within the default layout template.

Things don’t work out? Please don’t hesitate to leave a comment below or shoot us a tweet @futurestud_io.


Additional Ressources

Explore the Library

Find interesting tutorials and solutions for your problems.