Nick
Nick

Reputation: 932

What is the simplest way to link between .hbs files using handlebars?

I am a complete beginner to handlebars and am trying to modify the simple handlebars template taken from the example on glitch.com

I would like to be able to link between .hbs files as I would link between .html files but when I try however I am given the message cannot GET followed by whatever file I give to it.

Here is a grab of my overall structure for ref;

enter image description here

Here is the index.hbs files I am working with

<!DOCTYPE html>
<html>
  {{> head }}
  <body>
    <a href="views/secondpage.hbs">Link to second page</a>
  </body>
</html>

Which I would like to link to (for example) this secondpage.hbs file;

<!DOCTYPE html>
<html>
  {{> head }}
  <body>
    <a href="views/index.hbs">Link back to index</a>
  </body>
</html>

Here is the code in my server.js file

// Generic node.js express init:
const express = require('express');
const app = express();
app.use(express.static('public'));

const hbs = require('hbs');

hbs.registerPartials(__dirname + '/views/partials/');

app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');

app.get("/", (request, response) => {

  let dt = new Date();
  let data = {
    projectName: process.env.PROJECT_DOMAIN,
    luckyNumber: Math.floor(Math.random()*1000),
    serverTime: new Date(),
    ip: (request.headers["x-forwarded-for"]||"").split(",")[0]
  };

  data.json = JSON.stringify(data, null, 2);

  response.render('index', data);
});

let listener = app.listen(process.env.PORT, () => {
  console.log('Your app is listening on port ' + listener.address().port);
});

and the code in my watch.json

{
  "install": {
    "include": [
      "^package\\.json$",
      "^\\.env$"
    ]
  },
  "restart": {
    "exclude": [
      "^public/",
      "^dist/"
    ],
    "include": [
      "\\.js$",
      "\\.hbs$",
      "\\.json"
    ]
  },
  "throttle": 100
}

If any of the details of the other files is necessary to assist let me know and I can provide.

I appreciate I am probably thinking about this in the wrong way, I have looked at handlebars in more detail and experimented with helpers etc. but it seems overly complicated for what I am trying to achieve, I thought you could write basic html within an hbs file? I am looking for the most straightforward, generic solution to the problem of linking between views in handlebars.

FWIW I want to use handlebars in a pretty simple fashion, basically just looking to have the equivalent of php includes using partials instead, so if there is a better way to approach the creation of the app with that in mind I would be grateful for advice.

Upvotes: 1

Views: 13234

Answers (2)

DeveloQueer
DeveloQueer

Reputation: 11

Instead of using handlebars I used express-handlebars Terminal: npm i express-handlebars

Handlebars is a Middleware and functions as a Twig (Template Engine) so for your server I'd suggest:

// Generic node.js express init:
const express = require('express');
const app = express();
app.use(express.static('public'));

const exphbs = require('express-handlebars');

app.set('views', __dirname + '/views');
// added this part
app.engine('.hbs', exphbs ({
    defaultLayout: 'main',
    layoutsDir: ('views', __dirname + 'layouts'),
    partialsDir: ('views', __dirname 'partials'),
    extname: '.hbs'
}));
app.set('view engine', 'hbs')

app.get("/", (request, response) => {

  let dt = new Date();
  let data = {
    projectName: process.env.PROJECT_DOMAIN,
    luckyNumber: Math.floor(Math.random()*1000),
    serverTime: new Date(),
    ip: (request.headers["x-forwarded-for"]||"").split(",")[0]
  };

  data.json = JSON.stringify(data, null, 2);

  response.render('index', data);
});

let listener = app.listen(process.env.PORT, () => {
  console.log('Your app is listening on port ' + listener.address().port);
});

By doing this, you should have a file in your layouts folder named main.hbs where you will have that dynamic approach you're looking for. Something that stays the same for all pages. I will insert here a suggestion, feel free to adapt for your code.

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- CUSTOM CSS -->
        <link rel="stylesheet" href="/css/main.css">
      </head>
      <body>

        {{> navigation }}

        <main class="container p-5">
          {{{ body }}}
        </main>

      </body>
    </html>

Now when you create a navigation.hbs in your partials folder you will have the same frontend in all pages in the navigation. This is because we defined in the server.js our default template to be main.hbs. Whilst for your body, the triple hash ({{{}}}) inserts the components of the other .hbs files that you define. Don't forget to create a index.hbs file inside the views folder.

I learned the basics of hbs by following this tutorial (Note it's in Spanish). The tutorial produces this open-source project (which I am including in case it is useful).

Upvotes: 0

Diogo Capela
Diogo Capela

Reputation: 6570

Your code looks alright. What is the problem exactly? When you add {{> head}} partial to the index.hbs doesn't it render properly?

EDIT:

Okay, you have mainly 2 problems with your code:

  • You have no route defined on express linking to your /secondpage endpoint.
  • You are trying to link to a file <a href="views/secondpage.hbs">Link</a> instead of linking to an URL endpoint <a href="/secondpage">Link</a>.

To fix your code you would have to define the endpoint linking to the handlebars file, so you need to change your server.js file to something like this.

const express = require('express');
const hbs = require('hbs');

const app = express();

app.use(express.static('public'));
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
hbs.registerPartials(__dirname + '/views/partials/');

// 1st Page Route (URL Endpoint)

app.get('/', (request, response) => {
    const data = {
        projectName: process.env.PROJECT_DOMAIN,
        luckyNumber: Math.floor(Math.random() * 1000),
        serverTime: new Date(),
        ip: (request.headers['x-forwarded-for'] || '').split(',')[0],
    };
    data.json = JSON.stringify(data, null, 2);

    response.render('index', data);
});

// 2nd Page Route (URL Endpoint)

app.get('/secondpage', (request, response) => {
    response.render('secondpage');
});

const listener = app.listen(process.env.PORT, () => {
    console.log('Your app is listening on port ' + listener.address().port);
});

And then you need to fix your HTML links to this on index.hbs:

<!DOCTYPE html>
<html>
  {{> head }}
  <body>
    <a href="/secondpage">Link to second page</a>
  </body>
</html>

And this on secondpage.hbs:

<!DOCTYPE html>
<html>
  {{> head }}
  <body>
    <a href="/">Link back to index</a>
  </body>
</html>

Hope this helps you.

Upvotes: 4

Related Questions