如何在Express中使用Handlebars更改默认布局?

75
我正在使用Express 4.9.0和express-generator。
使用以下命令创建了一个样板文件:
express --hbs projectname

内置的手柄默认使用views/layout.hbs作为主页面。但我在我的app.js中看不到任何设置来更改这个行为。
我app.js中的一段代码:
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

1. 我如何在全局范围内更改默认布局? 2. 如果我想要有2个或3个不同的全局布局,该怎么办?

我想感谢Heihachi提出这个问题,我一直在寻找一种将handlebars与Express集成的方法,但是找不到一个好的例子。看着express-generator命令行,让我回忆起了如何使用hbs,并给了我一个简单而好的演示,这是我在网上找不到的。谢谢。 - Bharat
5个回答

133

在渲染调用的一部分中,您可以指定要使用的布局。如果您创建了一个名为other.hbs的新布局,则可以执行以下操作:

res.render('view', { title: 'my other page', layout: 'other' });

如果要在整个应用程序中覆盖此设置,可以使用:

app.set('view options', { layout: 'other' });

谢谢,但我不能全局地这样做吗? - Alexander Kim
1
@Heihachi 我已更新答案,包括全局选项。 - Timothy Strimple
如果我想要更多的全局布局,我只需要复制一个带有app.set()的行,对吗? - Alexander Kim
6
没事了,我找到了。这是与hbs有关的事情。如果没有指定,它会自动将布局设置为layout。在第115行代码中:var layout_filename = path.join(view_dir, layout || 'layout'); - shriek
@TimothyStrimple 我在想,如果我要发送到视图的数据包括名为layout的属性,并且我想使用res.render()来继承特定的布局。在需要在网站的不同部分中使用不同的资产和标题/页脚/侧边栏的情况下,多个全局布局可能会很方便。我可以发送JSON数据到布局视图以实现此目的,但那样我就无法向视图发送纯数据了。 - Bharat
显示剩余6条评论

15

从handlebars的自述文件中:

有两种方法可以设置默认布局:配置视图引擎的defaultLayout属性或设置Express locals app.locals.layout。

应该渲染到的布局可以通过为布局请求本地分配不同的值来每个请求进行覆盖。以下代码将使用无布局渲染“home”视图:

app.get('/', function (req, res, next) {
   res.render('home', {layout: false});
});
如果您想为特定的子路由设置默认布局,则可以在路由的顶部部分使用以下内容:
router.all('/*', function (req, res, next) {
    req.app.locals.layout = 'admin'; // set your layout here
    next(); // pass control to the next handler
    });

您还可以在初始化时设置默认布局:

// Create `ExpressHandlebars` instance with a default layout.
var hbs = exphbs.create({
    defaultLayout: 'main',
    helpers      : helpers,

    // Uses multiple partials dirs, templates in "shared/templates/" are shared
    // with the client-side of the app (see below).
    partialsDir: [
        'shared/templates/',
        'views/partials/'
        ]
    });

// Register `hbs` as our view engine using its bound `engine()` function.
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.engine('hbs', hbs.engine); app.set('view engine', 'hbs'); 在同一行执行此操作时,我的页面显示: 错误:ENOENT:没有这样的文件或目录,打开'/home/ray/Desktop/phoneSkill/functions/views/layouts/main.handlebars' 我想知道为什么会发生这种情况? 但是当我设置var hbs = exphbs.create({ defaultLayout: 'main.hbs', ...在同一行中它可以工作并显示页面。我想知道为什么会发生这种情况?难道它不应该自动解析main.hbs吗? - 0xAnon
1
@Coderboi 在配置文件中设置 extname: 'hbs' 来修改默认扩展名。例如:var hbs = exphbs.create({defaultLayout: 'main',extname: 'hbs',... - skg
在22中对我不起作用。 - fdrv

6
这应该现在能够正常工作了。
npm install express-handlebars

.
├── app.js
└── views
    ├── home.handlebars
    └── layouts
        └── main.handlebars

2 directories, 3 files

app.js

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

var app = express();

app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

app.get('/', function (req, res) {
    res.render('home');
});

app.listen(3000);

views/layouts/main.handlebars:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Example App</title>
</head>
<body>

    {{{body}}}

</body>
</html>

当我尝试这样做时,我得到了:ReferenceError: exphbs未定义。 - chikitin
1
你的代码中可能会漏掉这一行:var exphbs = require('express-handlebars'); - venkat7668
是的。我以为通过使用“快速安装”来安装它,会自动导入它。谢谢。 - chikitin

5
我希望你正在使用express-handlebars。这些指令是针对express-handlebars的。对于hbs,步骤略有不同。
步骤1:引入handlebars。
const exphbs = require('express-handlebars');

步骤2:注册handlebars模板引擎。在注册时,您可以进行配置

更改布局目录

const layoutPath = path.join(__dirname, './templates/layouts'); //you can build your desired path
app.engine('handlebars', exphbs({ layoutsDir: layoutPath }));

除了layoutsDir之外,还有其他可用选项:

interface ExphbsOptions {
    handlebars?: any;
    extname?: string;
    layoutsDir?: string;
    partialsDir?: any;
    defaultLayout?: string;
    helpers?: any;
    compilerOptions?: any;
}

步骤三:如果您想更改视图目录

const viewPath = path.join(__dirname, './templates/views');
app.set('views', viewPath);

步骤4:对于某些模板,如果您不想提供布局,则必须将其指定为layout: false。否则应用程序会崩溃。 如果需要,可以按以下方式进行配置。

app.get('/', (req, res, next) => {
    res.render('shop', { title: 'My Shop', layout: false })
});

了解更多关于express-handlebars的信息


2
如果您正在使用“express-handlebars”模块,则以下内容应该有效:
// ...
app.set("views", __dirname );

exphbs.ExpressHandlebars.prototype.layoutsDir = 'path/to/directory/';
app.engine('handlebars', exphbs({defaultView: 'name-of-template'}));

// ...

我是通过研究模块源代码发现的这个问题,事实证明这一行代码...

// express-handlebars/lib/express-handlebars.js (line 55 in v1.2.2)
ExpressHandlebars.prototype.layoutsDir  = 'views/layouts/';

这就是默认行为,始终在“{{你指定的路径}}/views/layouts/”中查找。

因此,如果您有其他目录结构或其他原因要覆盖它,可以使用我示例中的代码行。只需确保在实例化exphbs之前执行此操作。

如果您正在使用其他模块(我不确定有哪些),很可能它们具有类似的设置,可以通过一些技巧进行覆盖(只需在文件内容上运行“find”以查找“views/layouts/”即可)。

请注意,我将“app.set(“views”,__dirname);”保留为原样,以便在服务器目录中的任何位置保存模板,并像下面这样呈现它们:

res.render("moduleName/templateName");

更新到v2.0.1之后,上述方法将不再适用。相反,您可以将默认目录作为参数传递如下:

var hbs = exphbs.create({
  layoutsDir: 'app/server/',
  ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接