视图和布局###

视图通常表现为网站上的各个页面(它也可以表现为页面中AJAX局部加载的内容,或一封电子邮件,或页面上的任何东西)。默认情况下,Express会在views子目录中查找视图。布局是一种特殊的视图,事实上,它是一个用于模板的模板。布局是必不可少的,因为站点的大部分页面都有几乎相同的布局。例如,页面中必须有一个<html>元素和一个<title>元素,它们通常都会加载相同的CSS文件,诸如此类。你不想为每个网页复制代码,于是这就需要用到布局。让我们看看基本的布局文件:

<!doctype>
<html>
<head>
<title>Meadowlark Travel</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
{{{body}}}
</body>
</html>

请注意标记内的文本:{{{body}}}。这样视图引擎就知道在哪里渲染的内容了。一定要用三重大括号而不是两个,因为视图很可能包含HTML,我们并不想让Handlebars试图去转义它。注意,在哪里放置{{{body}}}并没有限制。此外,常见的网页元素,如页眉和页脚,通常也在布局中,而不在视图中。举例如下:

<!-- ... -->
<body>
<div class="container">
<header><h1>Meadowlark Travel</h1></header>
{{{body}}}
<footer>&copy;{{copyrightYear}} Meadowlark Travel</footer>
</div>
</body>

由于执行的顺序,你可以向视图中传递一个叫作body的属性,而且它会在视图中正确渲染。然而,当布局被渲染时,body的值会被已渲染的视图覆盖。

在Express中使用(或不使用)布局###

  • 当我们创建视图引擎时,会指定一个默认的布局:
var handlebars = require('express3-handlebars').create({ defaultLayout: 'main' });
  • 默认情况下,Express会在views子目录中查找视图,在views/layouts下查找布局。所以如果有一个叫作views/foo.handlebars的视图,可以这样渲染它:
app.get('/foo', function(req, res){
res.render('foo');
});

它会使用views/layouts/main.handlebars作为布局。

  • 如果你根本不想使用布局(这意味着在视图中你不得不拥有所有的样板文件),可以在上下文中指定layout: null:
app.get('/foo', function(req, res){
res.render('foo', { layout: null });
});
  • 如果你想使用一个不同的模板,可以指定模板名称:
app.get('/foo', function(req, res){
res.render('foo', { layout: 'microsite' });
});

这样就会使用布局views/layouts/microsite.handlebars来渲染视图了。

局部文件###

很多时候,有些组成部分(在前端界通常称为“组件”)需要在不同的页面重复使用。使用模板来实现这一目标的唯一方法是使用局部文件(partial,如此命名是因为它们并不渲染整个视图或整个网页)。

  • 首先,创建一个局部文件,views/partials/weather.handlebars:
<div class="weatherWidget">
{{#each partials.weather.locations}}
<div class="location">
<h3>{{name}}</h3>
<a href="{{forecastUrl}}">
<img src="{{iconUrl}}" alt="{{weather}}">
{{weather}}, {{temp}}
</a>
</div>
{{/each}}
<small>Source: <a href="http://www.wunderground.com">Weather
Underground</a></small>
</div>

**请注意,我们使用partials.weather为开头来命名上下文。我们想在任何页面上使用局部文件,但上述做法实际上并不会将上下文传递给每一个视图,因此可以使用res.locals(对于任何视图可用)。但是我们并不想让个别的视图干扰指定的上下文,于是将所有的局部文件上下文都放在partials对象中。

**

  • 创建一个方法来获取当前天气数据:
function getWeatherData(){
return {
locations: [
{
name: 'Portland',
forecastUrl: 'http://www.wunderground.com/US/OR/Portland.html',
iconUrl: 'http://icons-ak.wxug.com/i/c/k/cloudy.gif',
weather: 'Overcast',
temp: '54.1 F (12.3 C)',
},
{
name: 'Bend',
forecastUrl: 'http://www.wunderground.com/US/OR/Bend.html',
iconUrl: 'http://icons-ak.wxug.com/i/c/k/partlycloudy.gif',
weather: 'Partly Cloudy',
temp: '55.0 F (12.8 C)',
},
{
name: 'Manzanita',
forecastUrl: 'http://www.wunderground.com/US/OR/Manzanita.html',
iconUrl: 'http://icons-ak.wxug.com/i/c/k/rain.gif',
weather: 'Light Rain',
temp: '55.0 F (12.8 C)',
},
],
};
}
  • 创建一个中间件给res.locals.partials对象添加这些数据:
app.use(function(req, res, next){
if(!res.locals.partials) res.locals.partials = {};
res.locals.partials.weather = getWeatherData();
next();
});
  • 在视图中使用这个局部文件。例如,为将我们的组件放在主页上,编辑views/home.handlebars:
<h2>Welcome to Meadowlark Travel!</h2>
{{> weather}}

语法{{> partial_name}}可以让视图中包含一个局部文件。express3-handlebars会在views/partials中寻找一个叫作partial_name.handle‐bars的视图(或是weather.handlebars)

express3-handlebars支持子目录,所以如果你有大量的局部文件,可以将它们组织在一起。例如,你有一些社交媒体局部文件,可以将它们放在views/partials/social目录下面,然后使用{{> social/facebook}}、{{>social/twitter}}等来引入它们。

段落###

我从微软的优秀模板引擎Razor中借鉴了段落(section)的概念。如果所有的视图在你的布局中都正好放在一个单独的元素里,布局会正常运转,但是当你的视图本身需要添加到布局的不同部分时会发生什么?一个常见的例子是,视图需要向<head>元素中添加一些东西,或是插入一段使用jQuery的<script>脚本(这意味着必须引入jQuery,由于性能原因,有时在布局中这是最后才做的事)。

Handlebars和express3-handlebars都没有针对于此的内置方法。幸运的是,Handlebars的辅助方法让整件事情变得简单起来。当我们实例化Handlebars对象时,会添加一个叫作section的辅助方法:

var handlebars = require('express3-handlebars').create({
defaultLayout:'main',
helpers: {
section: function(name, options){
if(!this._sections) this._sections = {};
this._sections[name] = options.fn(this);
return null;
}
}
});

现在我们可以在视图中使用section辅助方法了。让我们创建一个视图(views/jquerytest. handlebars),在<head>中添加一些东西,并添加一段使用jQuery的脚本:

{{#section 'head'}}
<!-- we want Google to ignore this page -->
<meta name="robots" content="noindex">
{{/section}} <h1>Test Page</h1>
<p>We're testing some jQuery stuff.</p> {{#section 'jquery'}}
<script>
$('document').ready(function(){
$('h1').html('jQuery Works');
});
</script>
{{/section}}

现在在这个布局里,我们可以像放置{{{body}}}一样放置一个段落:

<!doctype html>
<html>
<head>
<title>Meadowlark Travel</title>
{{{_sections.head}}}
</head>
<body>
{{{body}}}
<script src="http://code.jquery.com/jquery-2.0.2.min.js"></script>
{{{_sections.jquery}}}
</body>
</html>

客户端Handlebars###

AJAX调用可以返回HTML片段,并将其原样插入DOM中,但是客户端Handlebars允许我们使用JSON数据接收AJAX调用结果,并将其格式化以适应我们的网站。因此,在与第三方API(返回JSON数据,而不是适应你网站的格式化HTML文本)通信时尤其有用。

在客户端使用Handlebars之前,我们需要加载Handlebars。我们既可以将Handlebars放在静态资源中引入,也可以使用一个CDN

{{#section 'head'}}
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/
handlebars.min.js"></script>
{{/section}}

现在需要找个地方放模板,一种方法是使用在HTML中已存在的元素,最好是一个隐藏的元素。你可以将它放在<head>中的<script>元素里。这看起来有点奇怪,但是运行良好:

{{#section 'head'}}
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.min.js"></script> <script id="nurseryRhymeTemplate" type="text/x-handlebars-template">
Marry had a little <b>\{{animal}}</b>, its <b>\{{bodyPart}}</b>
was <b>\{{adjective}}</b> as <b>\{{noun}}</b>.
</script>
{{/section}}

请注意,我们必须转义至少一个大括号,否则,服务器端视图会尝试对其进行替换。

在使用模板之前,我们需要编译它:

{{#section 'jquery'}}
$(document).ready(function(){
var nurseryRhymeTemplate = Handlebars.compile(
$('#nurseryRhymeTemplate').html());
});
{{/section}}

我们需要一个放置已渲染模板的地方。出于测试的目的,我们添加两个按钮,一个通过JavaScript来直接渲染,另一个通过AJAX调用来渲染:

<div id="nurseryRhyme">Click a button....</div>
<hr>
<button id="btnNurseryRhyme">Generate nursery rhyme</button>
<button id="btnNurseryRhymeAjax">Generate nursery rhyme from AJAX</button>

最后是渲染模板的代码:

{{#section 'jquery'}}
<script>
$(document).ready(function(){ var nurseryRhymeTemplate = Handlebars.compile(
$('#nurseryRhymeTemplate').html()); var $nurseryRhyme = $('#nurseryRhyme'); $('#btnNurseryRhyme').on('click', function(evt){
evt.preventDefault();
$nurseryRhyme.html(nurseryRhymeTemplate({
animal: 'basilisk',
bodyPart: 'tail',
adjective: 'sharp',
noun: 'a needle'
}));
}); $('#btnNurseryRhymeAjax').on('click', function(evt){
evt.preventDefault();
$.ajax('/data/nursery-rhyme', {
success: function(data){
$nurseryRhyme.html(
nurseryRhymeTemplate(data))
}
});
});
});
</script>
{{/section}}

针对nursery rhyme页和AJAX调用的路由处理程序:

app.get('/nursery-rhyme', function(req, res){
res.render('nursery-rhyme');
});
app.get('/data/nursery-rhyme', function(req, res){
res.json({
animal: 'squirrel',
bodyPart: 'tail',
adjective: 'bushy',
noun: 'heck',
});
});

**从本质上讲,Handlebars.compile接收一个模板,返回一个方法。这个方法接收一个上下文对象,返回一个已渲染字符串。所以一旦我们编译了模板,就可以像调用方法函数一样重用模板渲染。

**

express-9 Handlebars模板引擎(2)的更多相关文章

  1. 【转】在Express项目中使用Handlebars模板引擎

    原文:http://fraserxu.me/2013/09/12/Using-Handlebarsjs-with-Expressjs/ 最近在用Expressjs做一个项目,前后端都用它来完成.自己之 ...

  2. Handlebars模板引擎中的each嵌套及源码浅读

    若显示效果不佳,可移步到愚安的小窝 Handlebars模板引擎作为时下最流行的模板引擎之一,已然在开发中为我们提供了无数便利.作为一款无语义的模板引擎,Handlebars只提供极少的helper函 ...

  3. Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API

    A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...

  4. Handlebars模板引擎之高阶

    Helpers 其实在Handlebars模板引擎之进阶我想说if else的功能的,可是由于这个功能在我的开发中我觉的鸡肋没啥用,就直接不用了. 因为if else只能进行简单判断,如果条件参数返回 ...

  5. handlebars模板引擎使用初探1

    谈到handlebars,我们不禁产生疑问,为什么要使用这样的一个工具呢?它究竟能为我们带来什么样的好处?如何使用它呢? 一.handlebars可以干什么? 首先,我们来看一个案例: 有这样的htm ...

  6. express-8 Handlebars模板引擎(1)

    简介 使用JavaScript生成一些HTML document.write('<h1>Please Don\'t Do This</h1>'); document.write ...

  7. Handlebars 模板引擎之前后端用法

    前言 不知不觉间,居然已经这么久没有写博客了,坚持还真是世界上最难的事情啊. 不过我最近也没闲着,辞工换工.恋爱失恋.深圳北京都经历了一番,这有起有落的生活实在是太刺激了,就如拿着两把菜刀剁洋葱一样, ...

  8. express 4.x 模板引擎与express.static

    前提:要在express中使用模块引擎需要将要使用的模板引擎安装在本项目,当然,express也是要安装的.在下面实例中,我使用的模板引擎是pug(一起叫做jade) 我的目录结构如下: 根目录为st ...

  9. express中ejs模板引擎

    1.在 app.js 中通过以下两个语句设置了 引擎类型 和页面模板的位置: app.set('views', __dirname + '/views'); app.set('view engine' ...

随机推荐

  1. 【leetcode】 Interleaving String (hard)

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...

  2. 【编程题目】在一个字符串中找到第一个只出现一次的字符。如输入 abaccdeff,则输出 b。

    第 17 题(字符串):题目:在一个字符串中找到第一个只出现一次的字符.如输入 abaccdeff,则输出 b. 思路:此题非常容易. 最开始是想开辟一块空间存储每个字符出现的次数. 但转念一想,似乎 ...

  3. HDU 5898 odd-even number (数位DP) -2016 ICPC沈阳赛区网络赛

    题目链接 题意:一个数字,它每个数位上的奇数都形成偶数长度的段,偶数位都形成奇数长度的段他就是好的.问[L , R]的好数个数. 题解:裸的数位dp, 从高到低考虑每个数位, 状态里存下到当前位为止的 ...

  4. PDO(数据访问抽象层)

    自带事务功能,多条sql同时执行时,如果其中一条执行失败,那么所有的都执行失败.开启了事务,可以进行回滚操作,让程序变得更安全. 1.访问不同的数据库2.自带事务功能3.防止SQL注入:分两次发送 / ...

  5. java课后作业5

    [问题]随机生成10个数,填充一个数组,然后用消息框显示数组内容,接着计算数组元素的和,将结果也显示在消息框中. 设计思路: 1.申请一个长度为10的数组 2.计算机随机生成10个数,并赋给数组 3. ...

  6. August 12th 2016 Week 33rd Friday

    Everything is good in its season. 万物逢时皆美好. Every dog has its day. You are not in your best condition ...

  7. NPOI 1.2.4教程 –日期函数

    //Excel中有非常丰富的日期处理函数,在NPOI中同样得到了很好的支持.如下图: using NPOI.HSSF.UserModel; using NPOI.HPSF; using NPOI.PO ...

  8. java 学习笔记——网络(Socket)

    阅读方法:将网页放大到200%. 如果你用过用过word应该知道按住ctrl键使用鼠标滚轮缩放.

  9. 数据结构和算法 – 5.模式匹配和文本处理

    了使用正则表达式,需要把 RegEx 类引入程序.大家可以在 System.Text.RegularExpression 名字域中找到这种类.一旦把这种类导入了程序,就需要决定想要用 RegEx 类来 ...

  10. Android5.0如何正确启用isLoggable(二) 理分析

    转自:http://www.it165.net/pro/html/201506/43374.html 概要 在上文<Android 5.0 如何正确启用isLoggable(一)__使用详解&g ...