7

I am using express-handlebars to load dynamic content in NodeJS

inside app.js

const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const pp = require("./util/path.js");
const adminRoutes = require("./routes/admin");
const shopRoutes = require("./routes/shop");
const expressHbs = require("express-handlebars");
const app = express();

app.engine("hbs",expressHbs());
app.set("view engine", "hbs");
app.set("views", "views");

app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "public")));

app.use("/admin", adminRoutes.routes);
app.use(shopRoutes);

app.use((req, res, next) => {
  res.status(404).render("404", { pageTitle: "Page Not Found" });
});
app.listen(3001, "localhost", () => console.log("Listening on 3001 PORT"));

When expressHbs() function has no param it shows the following error

Error: ENOENT: no such file or directory, open 'C:\dev\nodejs\maximi_course\views\layouts\main.handlebars'

And when i pass an option object to it:

app.engine(
  "hbs",
  expressHbs({
    extname: "hbs",
    layoutsDir: path.join(__dirname, "views")
  })
);

It shows:

Error: ENOENT: no such file or directory, open 'C:\dev\nodejs\maximi_course\views\main.hbs'

I've searched for a solution but i got no result , Actually i am following a tutorial and i have done the same as teacher did but i got an error.

Lastly thing that i have tried is adding defaultLayout property and it works and load the default but when i change the url to another page it always load the same page which i set as default

Here is project folder and all its contents

Directory

Abdulrahman Falyoun
  • 3,676
  • 3
  • 16
  • 43
  • For those who arrive here while following the Udemy 'NodeJS: The Complete Guide Tutorial', the answer from @Marius M is going to be your fastest way around this problem – kdub1312 Jul 06 '22 at 02:38

11 Answers11

15

I had the same problem and fixed it by using the following method:

app.engine(
  "hbs",
  expressHbs({
    extname: "hbs",
    defaultLayout: false,
    layoutsDir: "views/layouts/"
  })
);
Mahmood Afzali
  • 304
  • 2
  • 5
11

express-handlebars.js@4.0.1 defines the following configuration options

function ExpressHandlebars(config) {
    // Config properties with defaults.
    utils.assign(this, {
        handlebars     : Handlebars,
        extname        : '.handlebars',
        layoutsDir     : undefined, // Default layouts directory is relative to `express settings.view` + `layouts/`
        partialsDir    : undefined, // Default partials directory is relative to `express settings.view` + `partials/`
        defaultLayout  : 'main',
        helpers        : undefined,
        compilerOptions: undefined,
    }, config);

If you do not use a default layout you can just set defaultLayout: false like below

app.engine(
  "hbs",
  expressHbs({
    extname: "hbs",
    defaultLayout: false
  })
);
Marius M
  • 181
  • 2
  • 5
  • This worked for me, changed this var hbs = exphbs.create({}); to: var hbs = exphbs.create({ extname: "hbs", defaultLayout: false }); – ssoward May 12 '20 at 04:26
  • This worked for me, and is probably what the OP was looking for. They said they were following a tutorial and the code snippet they posted exactly matches the code from the Udemy 'NODEJS: The Complete Guide' course. Somebody in that situation is looking for a way to work around any express-handlebars layout directory structure requirements and just get a basic stand-along file rendering – kdub1312 Jul 06 '22 at 02:33
9

Need to set defaultLayout as false, else "express-handlebars" will search for main.handlebars.

app.engine("hbs", expressHbs({ defaultLayout: false }));
app.set("view engine", "hbs");
app.set("views", "views");
4

Actually it's a bit annoying problem with express-handlebars but got it solved as following:

  • I had to create a standalone file which's not one of Routes and assign its name to defaultLayout
  • I had to set the path to views/layouts/
  • I had to to define extname to the extension i had defined
app.engine(
  "hbs",
  expressHbs({
    extname: "hbs",
    defaultLayout: "main-layout",
    layoutsDir: "views/layouts/"
  })
);
Abdulrahman Falyoun
  • 3,676
  • 3
  • 16
  • 43
2

Node.js/Expressjs

Q. Error: ENOENT: no such file or directory, open 'S:\globle_express\views\layouts\main.handlebars'

Answer.

enter image description here

Rename your layout folder and do layouts

The folder structure of Expressjs should be like this
satendra singh
  • 129
  • 1
  • 11
1

Making defaultLayout to false will solve this problem. My code worked fine with me.

    app.engine('hbs',hbs(
  {
    extname:'hbs', 
    defaultLayout:false,
    layoutDir:__dirname+'/views/layout/', 
    partialsDir:__dirname+'/views/partials/'
  }
  ))

Better to keep folder name as layouts and file name inside is layout.hbs without 's'.

After renaming the folder, my previous code worked with me perfectly.

app.engine('hbs',hbs(
  {
    extname:'hbs', 
    defaultLayout:'layout',
    layoutDir:__dirname+'/views/layouts/', 
    partialsDir:__dirname+'/views/partials/'
  }
  ))
Yasir NV
  • 11
  • 2
0

There is no need of creating a layouts folder with a file within it.create 'views' folder and add the file. For instance 'form.handlebars' then do the following according to your code

    app.get('/' (req,res)=> {
      res.render("contact", {
      layout: false,
      name: req.body.name,
      quote: req.body.quote
      });
    })
Vidya L
  • 2,289
  • 4
  • 27
  • 42
0

Include this in your app.js

app.locals.layout = false;
Dima Kozhevin
  • 3,602
  • 9
  • 39
  • 52
0
 app.engine(
  'hbs',
  hbs.express4({
    defaultLayout: false,
    partialsDir: __dirname + '/views/partials'
  })
);

For express 4

mercury
  • 2,390
  • 3
  • 30
  • 39
0

You can simple add this:

app.engine('handlebars', exphbs.engine());
app.set('view engine', 'handlebars')

It worked in my case. Thanks:)

0

for express-handlebars 7 you could just do

import { engine } from 'express-handlebars';
app.engine('handlebars', engine({ defaultLayout: false }));

the engine function takes an optional parameter defaultLayout?: string|false; you could set it to false to avoid having to either create a layouts subfolder or passing {layout: false} in all of your render view function.

some_groceries
  • 1,164
  • 18
  • 38