查看同时支持 ajax 和直接调用的引擎
View engine that supports both ajax and direct calls
我正在寻找一种在 Node.js (Express) 中提供模板的方法,如果 "normally" 呈现为 HTML 并呈现为 JSON请求 ajax.
假设我在 swig 中有一个这样的模板:
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
<p>Hi, {{ name }}.</p>
{% endblock %}
现在,如果我使用浏览器发出正常请求,swig 将以 layout.html
作为布局呈现文件。
但是,如果我使用 ajax(或 ?partial
之类的参数)发出请求,我希望将块设置为 JSON,而不会在 layout.html
中呈现:
{
"title": "Default Page",
"body": "<p>Hi, Dave.</p>"
}
swig的选择是任意的,它可以是任何支持块布局继承的视图引擎。
我在 swig 和 Nunjucks 的文档中搜索了一种简单的方法来影响基于请求的模板渲染,但我没有想出一个主意(没有完全矫枉过正)。
针对您的具体情况简化了我的回答 use-case:
您可以通过使用 "middleware override".
轻松完成此操作
app.use(function (req, res, next) {
var oldRender = res.render.bind(res);
res.render = function (viewName, viewData) {
viewData.xhr = req.xhr;
if (!req.xhr) {
res.render(viewName, viewData);
} else {
res.render(viewName, viewData, function (err, renderedView) {
if (err) return next(err);
res.json({
url: req.url, // In case you want to use the HTML5 history API to update the URL in-place in the client's browser.
title: viewData.title,
view: renderedView
});
});
}
};
});
那么理想情况下,您的视图引擎应该能够执行以下操作:
{% if !xhr %}
{% extends 'layout.html' %}
{% endif %}
现在,当您调用 res.render('someView', { title: 'some title' });
时,它会编译一个完整的视图并将其发送下来,或者如果它是一个 XHR 请求,那么它会发送一个 JSON blob 和请求 URL、页面标题和呈现的视图作为字符串。然后就可以在客户端动态更新页面了。
如果您的模板引擎不支持条件 extends
那么您可以通过一些 "hack" 来解决这个问题。您需要两个视图 (pseudo-code):
// someView
<p>Hi, {{ name }}</p>
// someView_full
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
{% include 'someView' %}
{% endblock %}
然后修改您的中间件函数,在不是 AJAX 请求时将 "_full"
附加到视图名称。
if (!req.xhr) {
res.render(viewName + "_full", viewData);
} else { ... }
编辑: 还有另一种方法可以避免创建第二个小桥视图,甚至不必将每个 extends
包装在您的每个视图的条件中。我找到了这个方法here。原来你可以在布局文件中有条件地创建内容块,使这成为可能:
// layout
{% if !xhr %}
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
{% else %}
{% block body %}{% endblock %}
{% endif %}
这样布局文件将决定是否包含周围的标记。
PS - req.xhr
仅在发出 AJAX 请求的客户端框架包含 x-requested-with
header set to "XMLHttpRequest" 时才有效(提示:大多数情况下都是如此)。如果您的框架不包含 header 那么可能有一个选项可以切换,或者至少您应该能够设置自定义 headers 并自己包含它。
您可以像您在问题中建议的那样使用查询字符串变量,但是使用此请求 header 是与服务器通信的标准约定,该请求是 AJAX 并且通常被转换在 client-side 框架中默认打开。这就是 Express 内置 req.xhr
的原因:)
我正在寻找一种在 Node.js (Express) 中提供模板的方法,如果 "normally" 呈现为 HTML 并呈现为 JSON请求 ajax.
假设我在 swig 中有一个这样的模板:
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
<p>Hi, {{ name }}.</p>
{% endblock %}
现在,如果我使用浏览器发出正常请求,swig 将以 layout.html
作为布局呈现文件。
但是,如果我使用 ajax(或 ?partial
之类的参数)发出请求,我希望将块设置为 JSON,而不会在 layout.html
中呈现:
{
"title": "Default Page",
"body": "<p>Hi, Dave.</p>"
}
swig的选择是任意的,它可以是任何支持块布局继承的视图引擎。
我在 swig 和 Nunjucks 的文档中搜索了一种简单的方法来影响基于请求的模板渲染,但我没有想出一个主意(没有完全矫枉过正)。
针对您的具体情况简化了我的回答 use-case:
您可以通过使用 "middleware override".
轻松完成此操作app.use(function (req, res, next) {
var oldRender = res.render.bind(res);
res.render = function (viewName, viewData) {
viewData.xhr = req.xhr;
if (!req.xhr) {
res.render(viewName, viewData);
} else {
res.render(viewName, viewData, function (err, renderedView) {
if (err) return next(err);
res.json({
url: req.url, // In case you want to use the HTML5 history API to update the URL in-place in the client's browser.
title: viewData.title,
view: renderedView
});
});
}
};
});
那么理想情况下,您的视图引擎应该能够执行以下操作:
{% if !xhr %}
{% extends 'layout.html' %}
{% endif %}
现在,当您调用 res.render('someView', { title: 'some title' });
时,它会编译一个完整的视图并将其发送下来,或者如果它是一个 XHR 请求,那么它会发送一个 JSON blob 和请求 URL、页面标题和呈现的视图作为字符串。然后就可以在客户端动态更新页面了。
如果您的模板引擎不支持条件 extends
那么您可以通过一些 "hack" 来解决这个问题。您需要两个视图 (pseudo-code):
// someView
<p>Hi, {{ name }}</p>
// someView_full
{% extends 'layout.html' %}
{% block title %}Default Page{% endblock %}
{% block body %}
{% include 'someView' %}
{% endblock %}
然后修改您的中间件函数,在不是 AJAX 请求时将 "_full"
附加到视图名称。
if (!req.xhr) {
res.render(viewName + "_full", viewData);
} else { ... }
编辑: 还有另一种方法可以避免创建第二个小桥视图,甚至不必将每个 extends
包装在您的每个视图的条件中。我找到了这个方法here。原来你可以在布局文件中有条件地创建内容块,使这成为可能:
// layout
{% if !xhr %}
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
{% else %}
{% block body %}{% endblock %}
{% endif %}
这样布局文件将决定是否包含周围的标记。
PS - req.xhr
仅在发出 AJAX 请求的客户端框架包含 x-requested-with
header set to "XMLHttpRequest" 时才有效(提示:大多数情况下都是如此)。如果您的框架不包含 header 那么可能有一个选项可以切换,或者至少您应该能够设置自定义 headers 并自己包含它。
您可以像您在问题中建议的那样使用查询字符串变量,但是使用此请求 header 是与服务器通信的标准约定,该请求是 AJAX 并且通常被转换在 client-side 框架中默认打开。这就是 Express 内置 req.xhr
的原因:)