视图和布局###

视图通常表现为网站上的各个页面(它也可以表现为页面中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. java转换json需要导入的jar包,org/apache/commons/lang/exception/NestableRuntimeException

    缺少相应jar包都会有异常,根据异常找jar包导入......     这里我说下lang包,因为这个包我找了好半天:   我用的是: commons-lang3-3.1.jar  出现异常: jav ...

  2. 用户登录流程详解 +volley(StringRequest)

    在实习期间由于要求使用volley,所以第一次开始接触volley,从一开始的迷茫陌生,到疯狂的查找各种资料,通过在项目中用到的实际问题,我想做一些总结,所以写了这篇文章.下面我将介绍我理解的用户登录 ...

  3. 【leetcode】Reverse Linked List II (middle)

    Reverse a linked list from position m to n. Do it in-place and in one-pass. For example:Given 1-> ...

  4. ASINetworkQueues(经典2)

    ASINetworkQueues, 它的delegate提供更为丰富的功能 提 供的更多的回调方法如下: a,requestDidStartSelector,请求发起时会调此方法,你可以在此方法中跟据 ...

  5. osg绘制一个球体

    //By smells2 at Lab 2012-02-21#include <osg/Group>#include <osg/Geode>#include <osg/S ...

  6. merge

    当两个DataFrame相加的时候,如果,其中一个不全则会相加产生NA,所以必须一次性将数据的索引索引确定下来,然后对所有数据重建索引然后,填充0,再相加.否则有数据的和没数据的相加结果都变为了NA, ...

  7. MVC – 14.ajax异步请求

    14.1.配置文件 14.2.AjaxHelper – 异步链接按钮 14.3.AjaxHelper – 异步表单 AjaxOptions常见属性: 14.4.AjaxOptions对象生成[对应]触 ...

  8. C#值类型与引用类型

    值类型(Value Type),值类型实例通常分配在线程的堆栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据.其在MSDN的定义为值类型直接包含它们的数据,值类型的 ...

  9. penghui_031413 Bat命令学习

    penghui_031413   Bat命令学习 基础部分:====================================================================== ...

  10. Acdream 1111:LSS(水题,字符串处理)

    LSS Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 128000/64000 KB (Java/Others) SubmitStati ...