├── .gitignore ├── static ├── images │ ├── noise.png │ ├── favicon.png │ ├── handlebars-mark.png │ ├── handlebars_logo.png │ ├── handlebars-devswag.png │ └── forkme_right_darkblue_121621.png ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── js │ ├── demo.js │ └── handlebars.runtime-v3.0.1.js ├── stylesheets │ ├── sunburst.css │ └── application.css └── css │ └── demo.css ├── s.js ├── posts ├── reference.md ├── block_helpers.md ├── builtin_helpers.md ├── precompilation.md ├── demo │ ├── HTML-escapes.md │ ├── demo.md │ ├── expressions-subexpressions.md │ ├── execution-1.md │ ├── expressions-whitespaccontrol-2.md │ ├── expressions-whitespaccontrol-1.md │ ├── SafeString.md │ ├── expressions-1.md │ ├── expressions-2.md │ ├── expressions-3.md │ ├── SafeString-escapeExpression.md │ └── property-not-a-valid-identifier.md ├── execution.md ├── expressions.md └── index.md ├── package.json ├── README.md ├── _template ├── demo.html └── default.html ├── reference.html ├── block_helpers.html ├── precompilation.html ├── builtin_helpers.html ├── demo ├── HTML-escapes.html ├── demo.html ├── expressions-subexpressions.html ├── execution-1.html ├── expressions-1.html ├── SafeString.html ├── expressions-whitespaccontrol-2.html ├── expressions-whitespaccontrol-1.html ├── expressions-2.html ├── expressions-3.html ├── SafeString-escapeExpression.html └── property-not-a-valid-identifier.html ├── execution.html ├── README.html ├── gulpfile.js ├── expressions.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /static/images/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/noise.png -------------------------------------------------------------------------------- /static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/favicon.png -------------------------------------------------------------------------------- /static/images/handlebars-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/handlebars-mark.png -------------------------------------------------------------------------------- /static/images/handlebars_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/handlebars_logo.png -------------------------------------------------------------------------------- /static/images/handlebars-devswag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/handlebars-devswag.png -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/images/forkme_right_darkblue_121621.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/images/forkme_right_darkblue_121621.png -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onface/handlebars-cn/HEAD/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /s.js: -------------------------------------------------------------------------------- 1 | var app = require('express')() 2 | app.use(require('express').static('./')) 3 | var port = 4784 4 | app.listen(port, function () { 5 | console.log('127.0.0.1:' + port) 6 | }) 7 | -------------------------------------------------------------------------------- /posts/reference.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 | 15 | 正在翻译中... ,你可以访问 [https://github.com/onface/handlebars-cn/](https://github.com/onface/handlebars-cn/) 参与翻译工作 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /posts/block_helpers.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 | 15 | 正在翻译中... ,你可以访问 [https://github.com/onface/handlebars-cn/](https://github.com/onface/handlebars-cn/) 参与翻译工作 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /posts/builtin_helpers.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 | 15 | 正在翻译中... ,你可以访问 [https://github.com/onface/handlebars-cn/](https://github.com/onface/handlebars-cn/) 参与翻译工作 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /posts/precompilation.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 | 15 | 正在翻译中... ,你可以访问 [https://github.com/onface/handlebars-cn/](https://github.com/onface/handlebars-cn/) 参与翻译工作 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /posts/demo/HTML-escapes.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 将模板修改为 `{{{html}}}` 可查看避免转义后的结果 15 | 16 | 17 | 22 | 23 | 24 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /posts/demo/demo.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 19 | 20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /posts/demo/expressions-subexpressions.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 19 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /posts/demo/execution-1.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 23 | 24 | 29 | 30 | 42 | 43 | 48 | 49 | -------------------------------------------------------------------------------- /posts/demo/expressions-whitespaccontrol-2.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | [删除空格示例](expressions-whitespaccontrol-1.html) 14 | 15 | 16 | 29 | 30 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /posts/demo/expressions-whitespaccontrol-1.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | [不删除空格示例](expressions-whitespaccontrol-2.html) 14 | 15 | 16 | 29 | 30 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /posts/demo/SafeString.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http://handlebars-cn.onface.live/", 3 | "version": "3.0.1", 4 | "description": "handlebars 中文网", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/onface/handlebars-cn" 12 | }, 13 | "keywords": [ 14 | "handlebarsjs" 15 | ], 16 | "author": "nimojs ", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/onface/handlebars-cn/issues" 20 | }, 21 | "homepage": "https://github.com/onface/handlebars-cn", 22 | "devDependencies": { 23 | "gulp": "^3.8.11", 24 | "gulp-cheerio": "^0.6.2", 25 | "gulp-markdown": "^1.0.0", 26 | "gulp-server-livereload": "^1.2.1", 27 | "gulp-tap": "^0.1.3", 28 | "gulp-util": "^3.0.4", 29 | "handlebars": "^3.0.1", 30 | "handlebars-helper-is": "^0.0.1", 31 | "highlight.js": "^8.5.0", 32 | "node-notifier": "^4.1.2" 33 | }, 34 | "dependencies": { 35 | "express": "^4.16.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /posts/demo/expressions-1.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 17 | 18 | 19 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /posts/demo/expressions-2.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 19 | 20 | 21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Handlebars 中文网](http://handlebars-cn.onface.live//) 2 | ================ 3 | 4 | 5 | 6 | [handlebras](http://handlebarsjs.com/) 官方文档的中文版 7 | 8 | - 中文版:[http://handlebars-cn.onface.live/](http://handlebars-cn.onface.live//) 9 | - 英文版:[http://www.handlebarsjs.com](http://www.handlebarsjs.com/) 10 | 11 | ## 直接提交 [issues](https://github.com/onface/handlebars-cn/issues) 报告翻译错误 12 | 打开 [issues](https://github.com/onface/handlebars-cn/issues/new) ,填写翻译错误对应的 URL 地址、错误文字、正确文字,原因。 13 | 14 | ## 使用 gulp 参与编辑 15 | 16 | 1. Fork 此项目 17 | 1. 使用 Github Clone 到本地 18 | 2. 打开命令行/终端 19 | 3. 跳转至对应目录 20 | 4. 命令行输入 `npm install` 21 | 5. 输入 `gulp` 回车 22 | 6. 开始监控 `/post/**/*.md` 文件修改,当 `*.md` 文件被修改时,将自动同步 对应的 `/**/*.html` 文件。 23 | 24 | 修改完成后使用 github 发送 pull requests 25 | 26 | ### HTML 特殊处理 27 | 28 | 因为官方英文文档中夹杂着一些带 class 的 `
` 所以通过 replace 做了一个替换。 29 | ```html 30 | 31 | 32 | Handlebars 模板看起来很像 HTML ,Handlebars 表达式嵌入在 HTML 中。 33 | ``` 34 | 35 | ```js 36 | html = html.replace(//g,'$1') 37 | ``` 38 | 39 | 可查看 [/posts/index.md](https://github.com/onface/handlebars-cn/edit/master/posts/index.md) 的源码以理解 `` 的用法。 40 | -------------------------------------------------------------------------------- /posts/demo/expressions-3.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 19 | 20 | 21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /_template/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ {{title}} } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ {{title}} } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:{{doc_text}} 23 | {{content}} 24 | 25 |
26 | 27 |

28 |
29 | 30 |
31 |
32 |
33 | 34 | -------------------------------------------------------------------------------- /_template/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 | 27 |
28 |
29 | 30 | Fork me on GitHub 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /posts/demo/SafeString-escapeExpression.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 使用 `escapeExpression` 配合 `SafeString` 输出链接 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /posts/execution.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 传入数据,执行 Handlebars 返回 渲染后的 HTML。 16 | 17 | ```html 18 | var source = $("#entry-template").html(); 19 | var template = Handlebars.compile(source); 20 | 21 | var context = {title: "My New Post", body: "This is my first post!"} 22 | var html = template(context); 23 | ``` 24 | 渲染结果 25 | 26 | ```html 27 |
28 |

My New Post

29 |
30 | This is my first post! 31 |
32 |
33 | ``` 34 | 35 | [示例](demo/execution-1.html) 36 | 37 | 38 | 39 | 选项 40 | ------- 41 | 42 | 这里的 `template()` 允许传入第二个参数作为选项 43 | 44 | The template function can be passed an options object as the second parameter which allows for customization: 45 | 46 | 47 | - `data` Pass in an object to define custom `@variable` private variables. 48 | - `helpers` Pass in to provide custom helpers in addition to the globally defined helpers. 49 | Values defined in this object will replace any values defined in the global object for the duration of the template execution. 50 | - `partials` Pass in to provide custom partials in addition to the globally defined partials. 51 | Values defined in this object will replace any values defined in the global object for the duration of the template execution. 52 | 53 | 54 | -------------------------------------------------------------------------------- /posts/demo/property-not-a-valid-identifier.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 如果你的表达式的标识符是特殊字符,可以使用 [ 和 ] 包裹标识符 15 | 16 | 17 | 18 | 19 | 29 | 30 | (注意:为了查看方便,此示例将 `10` 改为 `1` ) 31 | 32 | 33 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /reference.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 | 25 | 正在翻译中... ,你可以访问 https://github.com/onface/handlebars-cn/ 参与翻译工作 26 | 27 |
28 | 29 | 30 | 31 | 发现翻译错误?告诉我们。 32 | 33 |
34 |
35 |
36 | 37 | Fork me on GitHub 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /block_helpers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 | 25 | 正在翻译中... ,你可以访问 https://github.com/onface/handlebars-cn/ 参与翻译工作 26 | 27 |
28 | 29 | 30 | 31 | 发现翻译错误?告诉我们。 32 | 33 |
34 |
35 |
36 | 37 | Fork me on GitHub 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /precompilation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 | 25 | 正在翻译中... ,你可以访问 https://github.com/onface/handlebars-cn/ 参与翻译工作 26 | 27 |
28 | 29 | 30 | 31 | 发现翻译错误?告诉我们。 32 | 33 |
34 |
35 |
36 | 37 | Fork me on GitHub 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /builtin_helpers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 | 25 | 正在翻译中... ,你可以访问 https://github.com/onface/handlebars-cn/ 参与翻译工作 26 | 27 |
28 | 29 | 30 | 31 | 发现翻译错误?告诉我们。 32 | 33 |
34 |
35 |
36 | 37 | Fork me on GitHub 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /demo/HTML-escapes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 避免自动HTML转义 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 避免自动HTML转义 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:Handlebars 会转义HTML,你可以使用 {{{ 避免转义 23 | 24 |

将模板修改为 {{{html}}} 可查看避免转义后的结果

25 | 26 | 31 | 32 | 33 | 40 | 41 | 42 | 46 | 47 |
48 | 49 |

50 |
51 | 52 |
53 |
54 |
55 | 56 | -------------------------------------------------------------------------------- /demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 深入理解JavaScript-replace } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 深入理解JavaScript-replace } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:demo 23 | 24 | 25 | 30 | 31 | 32 | 46 | 47 | 48 | 54 | 55 |
56 | 57 |

58 |
59 | 60 |
61 |
62 |
63 | 64 | -------------------------------------------------------------------------------- /demo/expressions-subexpressions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 子表达式示例 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 子表达式示例 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:子表达式 23 | 24 | 25 | 30 | 31 | 32 | 35 | 36 | 37 | 49 | 50 |
51 | 52 |

53 |
54 | 55 |
56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /demo/execution-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ Handlebars 执行渲染 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ Handlebars 执行渲染 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档: 23 | 24 | 33 | 34 | 39 | 40 | 52 | 53 | 58 | 59 | 60 | 61 |
62 | 63 |

64 |
65 | 66 |
67 |
68 |
69 | 70 | -------------------------------------------------------------------------------- /demo/expressions-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 字符转义和数据安全性 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 字符转义和数据安全性 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:你也可以向 helper 传入字符串、数字或布尔值 23 | 24 | 25 | 28 | 29 | 30 | 37 | 38 | 39 | 49 | 50 |
51 | 52 |

53 |
54 | 55 |
56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /demo/SafeString.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ HTML 转义配合 SafeString } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ HTML 转义配合 SafeString } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:HTML 转义配合 SafeString 23 | 24 | 25 | 28 | 29 | 30 | 33 | 34 | 35 | 46 | 47 |
48 | 49 |

50 |
51 | 52 |
53 |
54 |
55 | 56 | -------------------------------------------------------------------------------- /demo/expressions-whitespaccontrol-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 控制换行和空格-不删除空格 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 控制换行和空格-不删除空格 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:控制空格 23 | 24 |

删除空格示例

25 | 26 | 39 | 40 | 41 | 51 | 52 | 53 | 57 | 58 |
59 | 60 |

61 |
62 | 63 |
64 |
65 |
66 | 67 | -------------------------------------------------------------------------------- /demo/expressions-whitespaccontrol-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 控制换行和空格-删除空格 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 控制换行和空格-删除空格 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:控制空格 23 | 24 |

不删除空格示例

25 | 26 | 39 | 40 | 41 | 51 | 52 | 53 | 57 | 58 |
59 | 60 |

61 |
62 | 63 |
64 |
65 |
66 | 67 | -------------------------------------------------------------------------------- /demo/expressions-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 字符转义和数据安全性 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 字符转义和数据安全性 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:你还可以使用 story.text 来渲染动态文本 23 | 24 | 25 | 30 | 31 | 32 | 42 | 43 | 44 | 56 | 57 |
58 | 59 |

60 |
61 | 62 |
63 |
64 |
65 | 66 | -------------------------------------------------------------------------------- /demo/expressions-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 字符转义和数据安全性 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 字符转义和数据安全性 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:helpers 支持传入任意顺序的 key-value 23 | 24 | 25 | 30 | 31 | 32 | 42 | 43 | 44 | 60 | 61 |
62 | 63 |

64 |
65 | 66 |
67 |
68 |
69 | 70 | -------------------------------------------------------------------------------- /static/js/demo.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $showerror = $('#showerror'); 3 | var fShowError = function (str) { 4 | $showerror.show().html(str) 5 | } 6 | var fHeredoc = function (fn) { 7 | // 1. 移除起始的 function(){ /*! 8 | // 2. 移除末尾的 */ } 9 | // 3. 移除起始和末尾的空格 10 | return fn.toString() 11 | .replace(/^[^\/]+\/\*!?/, '') 12 | .replace(/\*\/[^\/]+$/, '') 13 | .replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '') // .trim() 14 | } 15 | $('script.show,textarea,show').each(function () { 16 | var $this = $(this); 17 | $this.html($.trim($this.html())); 18 | }) 19 | 20 | var $result = $('#result'); 21 | var $expandingPre = $('#expandingPre'); 22 | var $dom = $('#dom'); 23 | var $helper = $('#helper'); 24 | var $data = $("#data"); 25 | var $htmlbody = $('html,body'); 26 | if ($data.length !== 0) { 27 | (function () { 28 | var source = $('#source').html(); 29 | console.log('模板:\r\n'); 30 | console.log(source); 31 | console.log('\r\n'); 32 | 33 | var sData = $.trim($data.html()); 34 | if (sData === '') { 35 | sData = '{}'; 36 | } 37 | try { 38 | data = $.parseJSON(sData); 39 | } catch (err) { 40 | $htmlbody.stop(false,true).animate({ 41 | 'scrollTop': $data.offset().top 42 | }) 43 | $data 44 | .fadeTo(50,.8).delay(100).fadeTo(50,1).delay(100).fadeTo(50,.8).delay(100).fadeTo(50,1) 45 | .delay(100).fadeTo(50,.8).delay(100).fadeTo(50,1); 46 | fShowError('数据源 JSON 格式错误,请仔细检查并修改,JSON 中属性名和字符串值都需要使用 "name" 包裹。JSON教程') 47 | throw err; 48 | } 49 | 50 | console.log('JSON字符串:\r\n'); 51 | console.log(sData); 52 | console.log('数据:\r\n') 53 | console.log(data); 54 | console.log('\r\n'); 55 | 56 | var template = Handlebars.compile(source); 57 | var result = template(data); 58 | console.log('渲染结果:\r\n'); 59 | console.log(result); 60 | if (typeof result === 'object') { 61 | result = result.string; 62 | } 63 | $expandingPre.html(Handlebars.escapeExpression(result)); 64 | $result.html(result); 65 | $dom.html(result); 66 | })() 67 | } 68 | }) -------------------------------------------------------------------------------- /demo/SafeString-escapeExpression.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ helper 的数据安全性 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ helper 的数据安全性 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:使用 helper 输出链接 23 | 24 |

使用 escapeExpression 配合 SafeString 输出链接

25 | 26 | 31 | 32 | 33 | 34 | 44 | 45 | 46 | 61 | 62 |
63 | 64 |

65 |
66 | 67 |
68 |
69 |
70 | 71 | -------------------------------------------------------------------------------- /demo/property-not-a-valid-identifier.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Handlebars 中文网:{ 表达式的属性包含特殊字符时的使用方法 } 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

14 | Handlebars 中文网:{ 表达式的属性包含特殊字符时的使用方法 } 15 | 16 | 18 | 19 |

20 |
21 |
22 | 建议将此页面保存至本地修改相关代码以帮助理解,与本示例相关的文档:如果你的表达式的标识符是特殊字符,可以使用 [ 和 ] 包裹标识符 23 | 24 |

如果你的表达式的标识符是特殊字符,可以使用 [ 和 ] 包裹标识符

25 | 26 | 36 | 37 |

(注意:为了查看方便,此示例将 10 改为 1

38 | 39 | 71 | 72 | 73 | 77 | 78 |
79 | 80 |

81 |
82 | 83 |
84 |
85 |
86 | 87 | -------------------------------------------------------------------------------- /execution.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 |

传入数据,执行 Handlebars 返回 渲染后的 HTML。

25 |
var source   = $("#entry-template").html();
26 | var template = Handlebars.compile(source);
27 | 
28 | var context = {title: "My New Post", body: "This is my first post!"}
29 | var html    = template(context);
30 | 
31 |

渲染结果

32 |
<div class="entry">
33 |   <h1>My New Post</h1>
34 |   <div class="body">
35 |     This is my first post!
36 |   </div>
37 | </div>
38 | 
39 |

示例

40 |
41 |

选项

42 |

这里的 template() 允许传入第二个参数作为选项

43 |

The template function can be passed an options object as the second parameter which allows for customization:

44 |
    45 |
  • data Pass in an object to define custom @variable private variables.
  • 46 |
  • helpers Pass in to provide custom helpers in addition to the globally defined helpers. 47 | Values defined in this object will replace any values defined in the global object for the duration of the template execution.
  • 48 |
  • partials Pass in to provide custom partials in addition to the globally defined partials. 49 | Values defined in this object will replace any values defined in the global object for the duration of the template execution.
  • 50 |
51 | 52 | 53 | 发现翻译错误?告诉我们。 54 | 55 |
56 |
57 |
58 | 59 | Fork me on GitHub 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /static/stylesheets/sunburst.css: -------------------------------------------------------------------------------- 1 | pre.sunburst { 2 | padding: 10px; 3 | font-size: 13px; 4 | font-family: Monaco, monospace; 5 | } 6 | 7 | pre.sunburst .DiffInserted { 8 | background-color: #253B22; 9 | color: #F8F8F8; 10 | } 11 | pre.sunburst .DiffHeader { 12 | background-color: #0E2231; 13 | color: #F8F8F8; 14 | font-style: italic; 15 | } 16 | pre.sunburst .CssPropertyValue { 17 | color: #F9EE98; 18 | } 19 | pre.sunburst .CCCPreprocessorDirective { 20 | color: #AFC4DB; 21 | } 22 | pre.sunburst .Constant { 23 | color: #3387CC; 24 | } 25 | pre.sunburst .DiffChanged { 26 | background-color: #4A410D; 27 | color: #F8F8F8; 28 | } 29 | pre.sunburst .Support { 30 | color: #9B859D; 31 | } 32 | pre.sunburst .MarkupList { 33 | color: #E1D4B9; 34 | } 35 | pre.sunburst .CssConstructorArgument { 36 | color: #8F9D6A; 37 | } 38 | pre.sunburst .Storage { 39 | color: #99CF50; 40 | } 41 | pre.sunburst .line-numbers { 42 | background-color: #DDF0FF; 43 | color: #000000; 44 | } 45 | pre.sunburst .CssClass { 46 | color: #9B703F; 47 | } 48 | pre.sunburst .StringConstant { 49 | color: #DDF2A4; 50 | } 51 | pre.sunburst .MarkupSeparator { 52 | background-color: #242424; 53 | color: #60A633; 54 | } 55 | pre.sunburst .MarkupUnderline { 56 | text-decoration: underline; 57 | color: #E18964; 58 | } 59 | pre.sunburst .CssAtRule { 60 | color: #8693A5; 61 | } 62 | pre.sunburst .MetaTagInline { 63 | color: #E0C589; 64 | } 65 | pre.sunburst .JEntityNameType { 66 | text-decoration: underline; 67 | } 68 | pre.sunburst .LogEntryError { 69 | background-color: #751012; 70 | } 71 | pre.sunburst .MarkupHeading { 72 | background-color: #632D04; 73 | color: #FEDCC5; 74 | } 75 | pre.sunburst .CssTagName { 76 | color: #CDA869; 77 | } 78 | pre.sunburst .SupportConstant { 79 | color: #CF6A4C; 80 | } 81 | pre.sunburst .MarkupQuote { 82 | background-color: #ECD091; 83 | color: #E1D4B9; 84 | font-style: italic; 85 | } 86 | pre.sunburst .DiffDeleted { 87 | background-color: #420E09; 88 | color: #F8F8F8; 89 | } 90 | pre.sunburst .CCCPreprocessorLine { 91 | color: #8996A8; 92 | } 93 | pre.sunburst .StringRegexpSpecial { 94 | color: #CF7D34; 95 | } 96 | pre.sunburst .EmbeddedSourceBright { 97 | background-color: #292929; 98 | } 99 | pre.sunburst .InvalidIllegal { 100 | background-color: #150B15; 101 | color: #FD5FF1; 102 | } 103 | pre.sunburst .MarkupRaw { 104 | background-color: #ABADB4; 105 | color: #578BB3; 106 | } 107 | pre.sunburst .SupportFunction { 108 | color: #DAD085; 109 | } 110 | pre.sunburst .CssAdditionalConstants { 111 | color: #DD7B3B; 112 | } 113 | pre.sunburst .MetaTagAll { 114 | color: #89BDFF; 115 | } 116 | pre.sunburst .StringRegexp { 117 | color: #E9C062; 118 | } 119 | pre.sunburst .StringEmbeddedSource { 120 | color: #DAEFA3; 121 | } 122 | pre.sunburst .EntityInheritedClass { 123 | color: #9B5C2E; 124 | font-style: italic; 125 | } 126 | pre.sunburst .MarkupComment { 127 | color: #F67B37; 128 | font-style: italic; 129 | } 130 | pre.sunburst .MarkupBold { 131 | font-weight: bold; 132 | color: #E9C062; 133 | } 134 | pre.sunburst .CssId { 135 | color: #8B98AB; 136 | } 137 | pre.sunburst .CssPseudoClass { 138 | color: #8F9D6A; 139 | } 140 | pre.sunburst .JCast { 141 | color: #676767; 142 | font-style: italic; 143 | } 144 | pre.sunburst .StringVariable { 145 | color: #8A9A95; 146 | } 147 | pre.sunburst .String { 148 | color: #65B042; 149 | } 150 | pre.sunburst .Keyword { 151 | color: #E28964; 152 | } 153 | pre.sunburst { 154 | background-color: #000000; 155 | color: #F8F8F8; 156 | } 157 | pre.sunburst .LogEntry { 158 | background-color: #C7C7C7; 159 | } 160 | pre.sunburst .MarkupItalic { 161 | color: #E9C062; 162 | font-style: italic; 163 | } 164 | pre.sunburst .CssPropertyName { 165 | color: #C5AF75; 166 | } 167 | pre.sunburst .Namespaces { 168 | color: #E18964; 169 | } 170 | pre.sunburst .DoctypeXmlProcessing { 171 | color: #494949; 172 | } 173 | pre.sunburst .InvalidDeprecated { 174 | color: #FD5FF1; 175 | font-style: italic; 176 | } 177 | pre.sunburst .Variable { 178 | color: #3E87E3; 179 | } 180 | pre.sunburst .Entity { 181 | color: #89BDFF; 182 | } 183 | pre.sunburst .Comment { 184 | color: #AEAEAE; 185 | font-style: italic; 186 | } 187 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 |

Handlebars 中文网

23 |

24 |

handlebras 官方文档的中文版

25 | 29 |

直接提交 issues 报告翻译错误

30 |

打开 issues ,填写翻译错误对应的 URL 地址、错误文字、正确文字,原因。

31 |

使用 gulp 参与编辑

32 |
    33 |
  1. Fork 此项目
  2. 34 |
  3. 使用 Github Clone 到本地
  4. 35 |
  5. 打开命令行/终端
  6. 36 |
  7. 跳转至对应目录
  8. 37 |
  9. 命令行输入 npm install
  10. 38 |
  11. 输入 gulp 回车
  12. 39 |
  13. 开始监控 /post/**/*.md 文件修改,当 *.md 文件被修改时,将自动同步 对应的 /**/*.html 文件。
  14. 40 |
41 |

修改完成后使用 github 发送 pull requests

42 |

HTML 特殊处理

43 |

因为官方英文文档中夹杂着一些带 class 的 <div> 所以通过 replace 做了一个替换。

44 |
<!---<div class="bullet">-->
45 | 
46 | Handlebars 模板看起来很像 HTML ,Handlebars 表达式嵌入在 HTML 中。
47 | 
48 |
html = html.replace(/<!---([^-]+)-->/g,'$1')
49 | 
50 |

可查看 /posts/index.md 的源码以理解 <!---<tag>--> 的用法。

51 | 52 | 53 | 发现翻译错误?告诉我们。 54 | 55 |
56 |
57 |
58 | 59 | Fork me on GitHub 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | anchorClass : "glyphicon glyphicon-link" 3 | } 4 | 5 | var gulp = require('gulp') 6 | var markdown = require('gulp-markdown') 7 | var tap = require('gulp-tap') 8 | var gutil = require('gulp-util') 9 | var cheerio = require('gulp-cheerio') 10 | var highlight = require('highlight.js') 11 | 12 | var fs = require('fs') 13 | var Handlebars = require('handlebars') 14 | var notifier = require('node-notifier') 15 | var path = require('path') 16 | var handlebarshelperis = require('handlebars-helper-is') 17 | handlebarshelperis(Handlebars) 18 | var server = require('gulp-server-livereload') 19 | 20 | // .replace(rStringToRegExp,'\\$1')) 21 | // "[]" => "\[\]" 22 | var rStringToRegExp = /([.$^{[(|)*+?\/])/g 23 | var fStringToPrefixRegExp = function (str) { 24 | return new RegExp( 25 | '^' + ( 26 | str.replace(rStringToRegExp, '\\$1') 27 | ) 28 | ); 29 | } 30 | 31 | // 渲染数据源:为了让-include 能访问数据源,每次渲染都将数据源绑定在此对象上。 32 | var oRenderData = {} 33 | 34 | /*! 35 | 让模板支持引入其他模板作为模块 36 | {{-include header}} 37 | 加载 _template/header.html 到当前代码行 38 | */ 39 | Handlebars.registerHelper('include', function (path) { 40 | path = '_template/' + path + '.html' 41 | if (fs.existsSync(path)) { 42 | return Handlebars.compile( 43 | new Handlebars.SafeString( 44 | fs.readFileSync(path, 'utf-8') 45 | ).toString() 46 | )(oRenderData) 47 | } else { 48 | var message = "Not find: {{include " + path + '}}' 49 | gutil.log(gutil.colors.red(message)) 50 | notifier.notify({ 51 | 'title': 'Gulp Not find', 52 | 'message': message 53 | }); 54 | return new Handlebars.SafeString('' + message + '') 55 | } 56 | }) 57 | 58 | /*! 59 | 比较2个参数 60 | {{-is a b "==" "!=="}} 61 | */ 62 | Handlebars.registerHelper('-is', function (a, b, yes, no) { 63 | return a == b?yes:no; 64 | }) 65 | 66 | 67 | /*! 68 | 编译 markdown 69 | @paths {string|array} - glob 或一个文件的绝对路径 gulp.src(paths) 70 | @dist {string} - 相当于 gulp.dest(dist) 中的 dist 71 | */ 72 | 73 | var compileMD = function (paths) { 74 | gulp.src(paths) 75 | .pipe(markdown({ 76 | highlight: function (code) { 77 | return highlight.highlightAuto(code).value 78 | } 79 | })) 80 | .pipe(cheerio(function($, file){ 81 | var $titles = $('h1,h2,h3,h4,h5,h6') 82 | $titles.removeAttr('id') 83 | var $a = $('a') 84 | $a.each(function () { 85 | var $this = $(this) 86 | var target = $this.attr('target'); 87 | if (!target){ 88 | $this.attr('target', '_blank') 89 | } 90 | }) 91 | })) 92 | .pipe(tap(function (file) { 93 | oRenderData = { 94 | URL_PATH: file.path.replace(fStringToPrefixRegExp(__dirname),'') 95 | } 96 | var html = file.contents.toString() 97 | // 将 替换为 98 | html = html.replace(/(href=['"][^"']+\.)md(['"])/g,'$1html$2') 99 | // 将 转换为锚记 100 | // oHash 用于防止 hash 重复 101 | html = html.replace(//g,'$1') 102 | // 将 替换为
(因为官方文档中存在一些带 class 的 div,此处需要兼容官方文档样式) 103 | var oHash = {} 104 | html = html.replace(//g, function () { 105 | var hash = arguments[1]; 106 | if (oHash[hash]) { 107 | gutil.log(gutil.colors.red(hash + 'is repeated')) 108 | notifier.notify({ 109 | 'title': 'hash repeated', 110 | 'message': Handlebars.escapeExpression(arguments[0]) 111 | }) 112 | } else { 113 | oHash[hash] = true; 114 | } 115 | return '' 116 | }) 117 | /*! 118 | 获取 markdown 中的 JSON信息,做为 _template/*.html 模板的渲染数据 119 | 130 | */ 131 | html = html.replace(/<\!--\_PAGEDATA([\s\S]*)?\_PAGEDATA-->/, function () { 132 | try { 133 | var pagedata = JSON.parse(arguments[1]) 134 | for (var key in pagedata) { 135 | oRenderData[key] = pagedata[key] 136 | } 137 | } catch (err) { 138 | gutil.log(gutil.colors.red(message)) 139 | notifier.notify({ 140 | 'title': ' Not find', 141 | 'message': message 142 | }); 143 | } 144 | 145 | // 取到 JSON 后删除 146 | return ''; 147 | }) 148 | 149 | // 将渲染数据的 content 属性定义为 *.md 编译后的 html 150 | oRenderData.content = html 151 | oRenderData._template = oRenderData._template || 'default' 152 | var templatePath = '_template/' + oRenderData._template + '.html' 153 | var filename = path.basename(file.path) 154 | if (fs.existsSync(templatePath)) { 155 | var template = fs.readFileSync(templatePath, 'utf-8') 156 | template = template.replace(/\{\{\s*content\s*\}\}/g,'{{{content}}}') 157 | .replace(/\{\{\s*include([^}]+)\}\}/g,'{{{$1}}}') 158 | var output = Handlebars.compile(template)(oRenderData) 159 | file.contents = new Buffer(output) 160 | gutil.log('Markdown ' + gutil.colors.green(filename)) 161 | } else { 162 | var message = "Not find:" + templatePath + '(' + filename + ')' 163 | 164 | gutil.log(gutil.colors.red(message)) 165 | notifier.notify({ 166 | 'title': 'Gulp Not find', 167 | 'message': message 168 | }); 169 | file.contents = new Buffer(message) 170 | } 171 | 172 | })) 173 | .pipe(gulp.dest(function(file) { 174 | return file.base.replace(fStringToPrefixRegExp(__dirname + '/posts'), __dirname) 175 | })) 176 | } 177 | 178 | gulp.task('watch-markdown', function () { 179 | gulp.watch('**/*.md', function (event) { 180 | compileMD(event.path) 181 | }) 182 | }) 183 | 184 | gulp.task('watch-handlebars', function () { 185 | gulp.watch('_template/*.html', function (event) { 186 | compileMD(['*.md','!(node_modules/**/*)']) 187 | }) 188 | }) 189 | 190 | gulp.task('posts', function (){ 191 | compileMD('posts/**/*.md') 192 | }) 193 | 194 | gulp.task('reload', function () { 195 | gulp.src('./') 196 | .pipe(server({ 197 | livereload: true, 198 | directoryListing: true, 199 | defaultFile: 'index.html', 200 | open: true 201 | })) 202 | }) 203 | 204 | 205 | gulp.task('default',['watch-markdown', 'watch-handlebars', 'reload']) 206 | 207 | -------------------------------------------------------------------------------- /posts/expressions.md: -------------------------------------------------------------------------------- 1 | 12 |
13 | 表达式是 Handlebars 模板的基本组成单位,单独使用 `{{mustache}}`,pass them to a Handlebars helper, or use them as values in hash arguments. 14 |
15 | 16 | 基本用法 17 | ------- 18 | 19 | 20 | 21 | 22 | 23 | 最简单的表达式是通过 `{{` 和 `}}` 包裹数据属性名 24 | 25 | ```html 26 |

{{title}}

27 | ``` 28 | ``{{title}}``会查找当前上下文 [(上下文?)](#) 中的 `title` 属性。 Block helpers 会改变当前上下文,但他不会改变表达式的语法。 29 | 30 | 31 | Actually, it means "look for a helper named title, then do the above", but we'll get to that soon enough. 32 | 33 | 34 | 35 | 36 | 37 | 你还可以使用 `.` 查找属性的子元素 38 | 39 | ```html 40 |

{{article.title}}

41 | ``` 42 | 这个示例的意思是:寻找当前上下文中的 `article` 属性,然后查找 `article` 属性的 `title` 属性。找到后输出 `title` 43 | 44 | handlebars也支持已经弃用的 `/` 分隔符,上面的表达式也可以写成: 45 | 46 | ```html 47 |

{{article/title}}

48 | ``` 49 | 50 | 51 | 52 | 53 | 54 | 标识符可以是除了以下字符以外的任何 unicode 字符: 55 | 56 | 空白 `!` `"` `#` `%` `&` `'` `(` `)` `*` `+` `,` `.` `/` `;` `<` `=` `>` `@` `[` `\` `]` `^` `{` `|` `}` `~` ` 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 如果你的标识符是特殊字符,可以使用 `[` 和 `]` 包裹标识符: 65 | 66 | ```html 67 | {{#each articles.[10].[#comments]}} 68 |

{{subject}}

69 |
70 | {{body}} 71 |
72 | {{/each}} 73 | ``` 74 | 模板中的 `articles.[10].[#comments]` 相当于 JavaScript 中的 `object.articles[10]["#comments"]`。[示例](demo/property-not-a-valid-identifier.html) 75 | 76 |
77 | `articles` 也可以是对象,如果是对象则访问 `articles` 的 `10` 属性。 78 |
79 | 80 | You may not include a closing `]` in a path-literal, but all other characters are fair game. 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Handlebars 会转义HTML,你可以使用 `{{{` 避免转义 89 | 90 | ``` 91 | {{{foo}}} 92 | ``` 93 | 94 | [示例](demo/HTML-escapes.html) 95 | 96 |
97 | 转义HTML 指的是: 98 |
 99 | & 会被转换为 &amp;
100 | < ~ &lt;
101 | > ~ &gt;
102 | " ~ &quot;
103 | ' ~ &#x27;
104 | ` ~ &#x60;
105 | 
106 | 107 |
108 | 109 | 110 | 111 | 112 | 113 | 114 | Helpers 115 | ------- 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | Helper 语法是简单的标识符后面紧跟着一个或多个参数(用空格分隔),每个参数都是 Handlebars 表达式。 124 | 125 | ``` 126 | {{{link story}}} 127 | ``` 128 | 129 | 如上示例,`link` 是 helper 的名称,`story` 是 helper 的参数。Handlebars evaluates parameters in exactly the same way described above in "Basic Usage". 130 | 131 | 132 | 133 | ``` 134 | Handlebars.registerHelper('link', function(object) { 135 | var url = Handlebars.escapeExpression(object.url), 136 | text = Handlebars.escapeExpression(object.text); 137 | 138 | return new Handlebars.SafeString( 139 | "" + text + "" 140 | ); 141 | }); 142 | ``` 143 | 当一个 helper 返回 HTML 字符串 时。你应该使用 `SafeString` 来避免转义,确保输出的是可渲染的HTML。 144 | 当使用 `SafeString` 时,应主动使用 `escapeExpression` 方法将不安全的数据过滤。 145 | 146 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 你也可以向 helper 传入字符串、数字或布尔值 160 | 161 | ``` 162 | {{{link "See more..." story.url}}} 163 | ``` 164 | 165 | 在这种情况下,把手将通过链接帮手两个参数:字符串“查看更多......”而在当前的背景下评估story.url的结果。 166 | 167 | 此时,Handlebars 会将字符串参数 `"See more..."` 和当前上下文的 `story.url` 传递给 helper link。 168 | 169 | ``` 170 | Handlebars.registerHelper('link', function(text, url) { 171 | url = Handlebars.escapeExpression(url); 172 | text = Handlebars.escapeExpression(text); 173 | 174 | return new Handlebars.SafeString( 175 | "" + text + "" 176 | ); 177 | }); 178 | 179 | ``` 180 | [示例](demo/expressions-1.html) 181 | 182 | 183 | 184 | 185 | 你还可以使用 `story.text` 来渲染动态文本 186 | 187 | ``` 188 | {{{link story.text story.url}}} 189 | ``` 190 | 191 | [示例](demo/expressions-2.html) 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | Handlebars helpers 支持传入任意顺序的 key-value (在本文档中被称为 hash arguments): 200 | 201 | ``` 202 | {{{link "See more..." href=story.url class="story"}}} 203 | ``` 204 | 205 |
206 | 此处 hash arguments 是 `href=story.url class="story"`。 207 | 208 |
    209 |
  • `href`是 key `story.url` 是 value
  • 210 |
  • `class`是 key `"story"` 是 value
  • 211 |
212 | 213 |
214 | 215 | hash arguments 必须是简单的标识符,value 必须是表达式。value 的值可以是标识符、路径和字符串。 216 | 217 | ``` 218 | Handlebars.registerHelper('link', function(text, options) { 219 | var attrs = []; 220 | 221 | for (var prop in options.hash) { 222 | attrs.push( 223 | Handlebars.escapeExpression(prop) + '="' 224 | + Handlebars.escapeExpression(options.hash[prop]) + '"'); 225 | } 226 | 227 | return new Handlebars.SafeString( 228 | "" + Handlebars.escapeExpression(text) + "" 229 | ); 230 | }); 231 | ``` 232 | 233 | 234 | 最终渲染结果: 235 | 236 | ``` 237 | See more... 238 | ``` 239 | 240 | 241 | 242 | 上例中的 `options.hash` 可访问 hash arguments 。[示例](demo/expressions-3.html) 243 | 244 | 245 | 246 | 247 | 248 | 249 | Handlebars also offers a mechanism for invoking a helper with a block of the template. Block helpers can then invoke that block zero or more times with any context it chooses. 250 | 251 | 了解更多:块 helpers 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 子表达式 261 | ------ 262 | 263 | 264 | 265 | 266 | 267 | Handlebars 支持子表达式, 子表达式的结果可作为父表达式的参数,字表达式使用括号分割 `(`。 268 | 269 | ``` 270 | {{outer-helper (inner-helper 'abc') 'def'}} 271 | ``` 272 | 273 | 在这个示例中,`'abc'` 作为 `inner-helper` 的参数被调用,而 `inner-helper` 的返回值作为 `outer-helper` 的第一个参数被调用(`'def'` 作为第二个参数被调用)。 274 | 275 | [示例](demo/expressions-subexpressions.html) 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 控制空格 286 | ------ 287 | 288 | 289 | 290 | 291 | 292 | 模板空白可由任何 mustache 语法括号中任意一侧添加 `~` 字符以删除。When applied all whitespace on that side will be removed up to the first handlebars expression or non-whitespace character on that side. 293 | 294 | ```html 295 | {{#each nav ~}} 296 | 297 | {{~#if test}} 298 | {{~title}} 299 | {{~else~}} 300 | Empty 301 | {{~/if~}} 302 | 303 | {{~/each}} 304 | ``` 305 | 306 | 307 | 数据为: 308 | 309 | ```js 310 | { 311 | "nav": [ 312 | {"url": "foo", "test": true, "title": "bar"}, 313 | {"url": "bar"} 314 | ] 315 | } 316 | ``` 317 | 318 | 最终会输出无换行和空格的内容 319 | 320 | ```html 321 | barEmpty 322 | ``` 323 | 324 | [示例](demo/expressions-whitespaccontrol-1.html) 325 | 326 | 327 | 328 | This expands the default behavior of stripping lines that are "standalone" helpers (only a block helper, comment, or partial and whitespace). 329 | 330 | 331 | ```html 332 | {{#each nav}} 333 | 334 | {{#if test}} 335 | {{title}} 336 | {{else}} 337 | Empty 338 | {{/if}} 339 | 340 | {{~/each}} 341 | ``` 342 | 343 | 渲染结果: 344 | 345 | ```html 346 | 347 | bar 348 | 349 | 350 | Empty 351 | 352 | ``` 353 | 354 | [示例](demo/expressions-whitespaccontrol-2.html) 355 | 356 | 357 | 358 | 359 | 360 | 361 | Id Tracking 362 | ------ 363 | 364 | 365 | 366 | 367 | 368 | Optionally, helpers can be informed of the paths that were used to lookup an argument for a given value. This mode may be enabled via the `trackIds` compiler flag. 369 | 370 | ``` 371 | {{foo bar.baz}} 372 | ``` 373 | 374 | would call the helper `foo` with the value of `bar.baz` but also will include the literal string `"bar.baz"` in the `ids` field on the `options` argument. 375 | 376 | This can be used for future lookup of parameters should it be necessary, but does add additional overhead. 377 | 378 | When this mode is enabled, all builtin helpers will generate a [@contextPath](reference.html#data-contextPath) variable that denotes the lookup path for the current context. It's highly recommended that generic helpers provide such a variable if they modify the context when executing their children. 379 | 380 | 381 | 382 | 383 | -------------------------------------------------------------------------------- /posts/index.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |
14 | 15 | 你可以使用 Handlebars 轻松的创建语义化模板。 16 | Handlebars 兼容 Mustache 模板。你可以在 Handlebars 中直接使用 Mustache 模板。 17 | 18 |
19 | Download: 3.0.1 20 | Download: runtime-3.0.1 21 | 22 | 快速入门 23 | --------------- 24 | 25 | 26 | 27 | Handlebars 模板看起来很像 HTML ,Handlebars 表达式嵌入在 HTML 中。 28 | 29 | ```html 30 |
31 |

{{title}}

32 |
33 | {{body}} 34 |
35 |
36 | ``` 37 | 38 | Handlebars 表达式以 `{{`属性名`}}` 的方式插入数据。 39 | 40 | 高级教程:表达式 41 | 42 | --------------------- 43 | 44 | 你可以在 HTML 中使用 ` 55 | ``` 56 | 57 | --------------------- 58 | 59 | 在 JavaScript 中获取 `` 中的模板并编译模板 60 | 61 | ```javascript 62 | var source = $("#entry-template").html(); 63 | var template = Handlebars.compile(source); 64 | ``` 65 | 66 | 67 | 建议初学者跳过预编译章节 68 | 69 | 70 | 还可以预编译你的模板。预编译后的模板只需使用 [handlebars.runtime.js](http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.runtime-v3.0.0.js) 渲染,这样可以提高性能并减少文件大小。在移动设备中这样做非常有意义(因为移动设备的性能和网络状态都没有PC好)。 71 | 72 | 73 | 高级教程:预编译 74 | 75 | --------------------- 76 | 77 | 传入数据,执行 Handlebars 返回 渲染后的 HTML。 78 | 79 | ```js 80 | var source = $("#entry-template").html(); 81 | var template = Handlebars.compile(source); 82 | 83 | var context = {title: "My New Post", body: "This is my first post!"}; 84 | var html = template(context); 85 | ``` 86 | 87 | 渲染结果 88 | 89 | ```html 90 |
91 |

My New Post

92 |
93 | This is my first post! 94 |
95 |
96 | ``` 97 | 98 | [示例](demo/execution-1.html) 99 | 100 | 高级教程: 执行 101 | 102 | HTML 转义 103 | ------------- 104 | 105 | 遇到 HTML标签时 Handlebars 会返回转义后的 HTML,如果你不希望被转义,可以使用 `{{{` 106 | 107 | ```html 108 |
109 |

{{title}}

110 |
111 | {{{body}}} 112 |
113 |
114 | ``` 115 | 116 | 渲染数据: 117 | 118 | ```js 119 | { 120 | title: "All about

Tags", 121 | body: "

This is a post about <p> tags

" 122 | } 123 | ``` 124 | 125 | 渲染结果: 126 | 127 | ```html 128 |
129 |

All About <p> Tags

130 |
131 |

This is a post about <p> tags

132 |
133 |
134 | ``` 135 | 136 | 137 | 138 | Handlebars 不会转义 `Handlebars.SafeString` 。如果你自定义了一个 helper 返回 HTML 代码,你需要返回 `new Handlebars.SafeString(result)` ,那么你需要手动对内容进行转义 139 | 140 | ```js 141 | Handlebars.registerHelper('link', function(text, url) { 142 | text = Handlebars.Utils.escapeExpression(text); 143 | url = Handlebars.Utils.escapeExpression(url); 144 | 145 | var result = '' + text + ''; 146 | 147 | return new Handlebars.SafeString(result); 148 | }); 149 | ``` 150 | 151 | `new Handlebars.SafeString()` 会标识传入参数是“安全的”,所以即使你不使用 `{{{` 。Handlebars 也不会转义。 152 | 153 | [示例](demo/SafeString.md) 154 | 155 | 156 | 块表达式 157 | ------- 158 | 159 | 块表达式允许你定义helper,用不同的数据上下文(context)调用一段模板。下面我们定义一个生成列表的helper: 160 | 161 | 快表达式允许你自定义 helper,使用当前传入参数作为上下文调用模板。 162 | 163 | 创建一个用于生产列表的快表达式 164 | 165 | ``` 166 | {{#list people}}{{firstName}} {{lastName}}{{/list}} 167 | ``` 168 | 169 | 渲染数据如下所示: 170 | 171 | ```js 172 | { 173 | people: [ 174 | {firstName: "Yehuda", lastName: "Katz"}, 175 | {firstName: "Carl", lastName: "Lerche"}, 176 | {firstName: "Alan", lastName: "Johnson"} 177 | ] 178 | } 179 | ``` 180 | 181 | we would create a helper named `list` to generate our HTML list. The helper receives the `people` as its first parameter, and an options hash as its second parameter. The options hash contains a property named `fn`, which you can invoke with a context just as you would invoke a normal Handlebars template. 182 | 183 | ```js 184 | Handlebars.registerHelper('list', function(items, options) { 185 | var out = "
    "; 186 | 187 | for(var i=0, l=items.length; i" + options.fn(items[i]) + ""; 189 | } 190 | 191 | return out + "
"; 192 | }); 193 | ``` 194 | 195 | When executed, the template will render: 196 | 197 | ```html 198 |
    199 |
  • Yehuda Katz
  • 200 |
  • Carl Lerche
  • 201 |
  • Alan Johnson
  • 202 |
203 | ``` 204 | 205 | Block helpers have more features, such as the ability to create an else section (used, for instance, by the built-in if helper). 206 | 207 | Since the contents of a block helper are escaped when you call `options.fn(context)`, Handlebars does not escape the results of a block helper. If it did, inner content would be double-escaped! 208 | 209 | Learn More: Block Helpers 210 | 211 | Handlebars Paths 212 | ---------------- 213 | 214 | Handlebars supports simple paths, just like Mustache. 215 | 216 | ```html 217 |

{{name}}

218 | ``` 219 | 220 | Handlebars also supports nested paths, making it possible to look up properties nested below the current context. 221 | 222 | ```html 223 |
224 |

{{title}}

225 |

By {{author.name}}

226 | 227 |
228 | {{body}} 229 |
230 |
231 | ``` 232 | 233 | That template works with this context 234 | 235 | 236 | ``` 237 | var context = { 238 | title: "My First Blog Post!", 239 | author: { 240 | id: 47, 241 | name: "Yehuda Katz" 242 | }, 243 | body: "My first post. Wheeeee!" 244 | }; 245 | ``` 246 | 247 | This makes it possible to use Handlebars templates with more raw JSON objects. 248 | 249 | Nested handlebars paths can also include `../` segments, which evaluate their paths against a parent context. 250 | 251 | 252 | 253 | ```html 254 |

Comments

255 | 256 |
257 | {{#each comments}} 258 |

{{title}}

259 |
{{body}}
260 | {{/each}} 261 |
262 | ``` 263 | 264 | Even though the link is printed while in the context of a comment, it can still go back to the main context (the post) to retrieve its permalink. 265 | 266 | The `../` path segment references the parent template scope, not one level up in the context. This is because block helpers can invoke a block with any context, so the notion of "one level up" isn't particularly meaningful except as a reference to the parent template scope. 267 | 268 | 269 | 270 | Handlebars also allows for name conflict resolution between helpers and data fields via a this reference: 271 | 272 | ```html 273 |

{{./name}} or {{this/name}} or {{this.name}}

274 | ``` 275 | 276 | Any of the above would cause the `name` field on the current context to be used rather than a helper of the same name. 277 | 278 | 279 | Template comments with {{!-- --}} or {{! }}. 280 | --------------------------------------------- 281 | 282 | You can use comments in your handlebars code just as you would in your code. Since there is generally some level of logic, this is a good practice. 283 | 284 | ```html 285 |
286 | {{!-- only output this author names if an author exists --}} 287 | {{#if author}} 288 |

{{firstName}} {{lastName}}

289 | {{/if}} 290 |
291 | ``` 292 | 293 | The comments will not be in the resulting output. If you'd like the comments to show up. Just use html comments, and they will be output. 294 | 295 | ```html 296 |
297 | {{! This comment will not be in the output }} 298 | 299 |
300 | ``` 301 | 302 | Any comments that must contain `}}` or other handlebars tokens should use the `{{!-- --}}` syntax. 303 | 304 | Helpers 305 | ------- 306 | 307 | Handlebars helpers can be accessed from any context in a template. You can register a helper with the `Handlebars.registerHelper` method. 308 | 309 | ```html 310 |
311 |

By {{fullName author}}

312 |
{{body}}
313 |

Comments

314 | {{#each comments}} 315 |

By {{fullName author}}

316 |
{{body}}
317 | {{/each}} 318 |
319 | ``` 320 | 321 | 322 | when using this context and helpers: 323 | 324 | ```js 325 | var context = { 326 | author: {firstName: "Alan", lastName: "Johnson"}, 327 | body: "I Love Handlebars", 328 | comments: [{ 329 | author: {firstName: "Yehuda", lastName: "Katz"}, 330 | body: "Me too!" 331 | }] 332 | }; 333 | 334 | Handlebars.registerHelper('fullName', function(person) { 335 | return person.firstName + " " + person.lastName; 336 | }); 337 | ``` 338 | 339 | results in: 340 | 341 | ```html 342 |
343 |

By Alan Johnson

344 |
I Love Handlebars
345 |

Comments

346 |

By Yehuda Katz

347 |
Me Too!
348 |
349 | ``` 350 | 351 | 352 | Helpers receive the current context as the `this` context of the function. 353 | 354 | ```html 355 |
    356 | {{#each items}} 357 |
  • {{agree_button}}
  • 358 | {{/each}} 359 |
360 | ``` 361 | 362 | when using this context and helpers: 363 | 364 | ```js 365 | var context = { 366 | items: [ 367 | {name: "Handlebars", emotion: "love"}, 368 | {name: "Mustache", emotion: "enjoy"}, 369 | {name: "Ember", emotion: "want to learn"} 370 | ] 371 | }; 372 | 373 | Handlebars.registerHelper('agree_button', function() { 374 | var emotion = Handlebars.escapeExpression(this.emotion), 375 | name = Handlebars.escapeExpression(this.name); 376 | 377 | return new Handlebars.SafeString( 378 | "" 379 | ); 380 | }); 381 | ``` 382 | 383 | results in: 384 | 385 | ```html 386 |
    387 |
  • 388 |
  • 389 |
  • 390 |
391 | ``` 392 | 393 | If your helper returns HTML that you do not want escaped, make sure to return a new Handlebars.SafeString. 394 | 395 | 396 | 397 | Built-In Helpers 398 | ---------------- 399 | 400 | Handlebars offers a variety of built-in helpers such as the if conditional and each iterator. 401 | 402 | Learn More: Built-In Helpers 403 | 404 | 405 | API Reference 406 | ------------- 407 | 408 | Handlebars offers a variety of APIs and utility methods for applications and helpers. 409 | 410 | Learn More: API Reference 411 | 412 | 413 | 29 | 30 |
31 |

最简单的表达式是通过 {{}} 包裹数据属性名

32 |
<h1>{{title}}</h1>
 33 | 
34 |

{{title}}会查找当前上下文 (上下文?) 中的 title 属性。 Block helpers 会改变当前上下文,但他不会改变表达式的语法。

35 |

Actually, it means "look for a helper named title, then do the above", but we'll get to that soon enough.

36 |
37 |
38 |

你还可以使用 . 查找属性的子元素

39 |
<h1>{{article.title}}</h1>
 40 | 
41 |

这个示例的意思是:寻找当前上下文中的 article 属性,然后查找 article 属性的 title 属性。找到后输出 title

42 |

handlebars也支持已经弃用的 / 分隔符,上面的表达式也可以写成:

43 |
<h1>{{article/title}}</h1>
 44 | 
45 |
46 |
47 |

标识符可以是除了以下字符以外的任何 unicode 字符:

48 |

空白 ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ { | } ~ `

49 |
50 |

52 |

如果你的标识符是特殊字符,可以使用 [] 包裹标识符:

53 |
{{#each articles.[10].[#comments]}}
 54 |   <h1>{{subject}}</h1>
 55 |   <div>
 56 |     {{body}}
 57 |   </div>
 58 | {{/each}}
 59 | 
60 |

模板中的 articles.[10].[#comments] 相当于 JavaScript 中的 object.articles[10]["#comments"]示例

61 |
62 | articles 也可以是对象,如果是对象则访问 articles10 属性。 63 |
64 | 65 |

You may not include a closing ] in a path-literal, but all other characters are fair game.

66 |
67 |
68 |

69 |

Handlebars 会转义HTML,你可以使用 {{{ 避免转义

70 |
{{{foo}}}
 71 | 

示例

72 |
73 | 转义HTML 指的是: 74 |
 75 | & 会被转换为 &amp;
 76 | < ~ &lt;
 77 | > ~ &gt;
 78 | " ~ &quot;
 79 | ' ~ &#x27;
 80 | ` ~ &#x60;
 81 | 
82 | 83 |
84 | 85 | 86 |
87 | 88 |

Helpers

89 | 90 | 91 |
Helper 语法是简单的标识符后面紧跟着一个或多个参数(用空格分隔),每个参数都是 Handlebars 表达式。

93 |
{{{link story}}}
 94 | 

如上示例,link 是 helper 的名称,story 是 helper 的参数。Handlebars evaluates parameters in exactly the same way described above in "Basic Usage".

95 |

96 |
Handlebars.registerHelper('link', function(object) {
 97 |   var url = Handlebars.escapeExpression(object.url),
 98 |       text = Handlebars.escapeExpression(object.text);
 99 | 
100 |   return new Handlebars.SafeString(
101 |     "<a href='" + url + "'>" + text + "</a>"
102 |   );
103 | });
104 | 

当一个 helper 返回 HTML 字符串 时。你应该使用 SafeString 来避免转义,确保输出的是可渲染的HTML。
当使用 SafeString 时,应主动使用 escapeExpression 方法将不安全的数据过滤。

105 |
106 | 确保输出<a> 而不是 &lt;a&gt;
并过滤 <span onclick="javascript:alert(1);">点击我</span> 这这危险数据。
若不明白请看 示例 107 |
108 | 109 |
110 |

112 |

你也可以向 helper 传入字符串、数字或布尔值

113 |
{{{link "See more..." story.url}}}
114 | 

在这种情况下,把手将通过链接帮手两个参数:字符串“查看更多......”而在当前的背景下评估story.url的结果。

115 |

此时,Handlebars 会将字符串参数 "See more..." 和当前上下文的 story.url 传递给 helper link。

116 |
Handlebars.registerHelper('link', function(text, url) {
117 |   url = Handlebars.escapeExpression(url);
118 |   text = Handlebars.escapeExpression(text);
119 | 
120 |   return new Handlebars.SafeString(
121 |     "<a href='" + url + "'>" + text + "</a>"
122 |   );
123 | });
124 | 

示例

125 |

126 |

你还可以使用 story.text 来渲染动态文本

127 |
{{{link story.text story.url}}}
128 | 

示例

129 |
130 |

132 |

Handlebars helpers 支持传入任意顺序的 key-value (在本文档中被称为 hash arguments):

133 |
{{{link "See more..." href=story.url class="story"}}}
134 | 
135 | 此处 hash arguments 是 href=story.url class="story"。 136 | 137 |
    138 |
  • href是 key story.url 是 value
  • 139 |
  • class是 key "story" 是 value
  • 140 |
141 | 142 |
143 | 144 |

hash arguments 必须是简单的标识符,value 必须是表达式。value 的值可以是标识符、路径和字符串。

145 |
Handlebars.registerHelper('link', function(text, options) {
146 |   var attrs = [];
147 | 
148 |   for (var prop in options.hash) {
149 |     attrs.push(
150 |         Handlebars.escapeExpression(prop) + '="'
151 |         + Handlebars.escapeExpression(options.hash[prop]) + '"');
152 |   }
153 | 
154 |   return new Handlebars.SafeString(
155 |     "<a " + attrs.join(" ") + ">" + Handlebars.escapeExpression(text) + "</a>"
156 |   );
157 | });
158 | 
159 |

最终渲染结果:

160 |
<a class="story" href="http://http://handlebars-cn.onface.live/">See more...</a>
161 | 
162 |

上例中的 options.hash 可访问 hash arguments 。示例

163 |
164 |
Handlebars also offers a mechanism for invoking a helper with a block of the template. Block helpers can then invoke that block zero or more times with any context it chooses.

166 |

了解更多:块 helpers

167 |
168 | 169 |

170 |

子表达式

171 | 172 | 173 |
174 |

Handlebars 支持子表达式, 子表达式的结果可作为父表达式的参数,字表达式使用括号分割 (

175 |
{{outer-helper (inner-helper 'abc') 'def'}}
176 | 

在这个示例中,'abc' 作为 inner-helper 的参数被调用,而 inner-helper 的返回值作为 outer-helper 的第一个参数被调用('def' 作为第二个参数被调用)。

177 |

示例

178 |
179 | 180 |

181 |

控制空格

182 | 183 | 184 |
185 |

模板空白可由任何 mustache 语法括号中任意一侧添加 ~ 字符以删除。When applied all whitespace on that side will be removed up to the first handlebars expression or non-whitespace character on that side.

186 |
{{#each nav ~}}
187 |   <a href="{{url}}">
188 |     {{~#if test}}
189 |       {{~title}}
190 |     {{~else~}}
191 |       Empty
192 |     {{~/if~}}
193 |   </a>
194 | {{~/each}}
195 | 
196 |

数据为:

197 |
{
198 |   "nav": [
199 |     {"url": "foo", "test": true, "title": "bar"},
200 |     {"url": "bar"}
201 |   ]
202 | }
203 | 
204 |

最终会输出无换行和空格的内容

205 |
<a href="foo">bar</a><a href="bar">Empty</a>
206 | 
207 |

示例

208 |

209 |

This expands the default behavior of stripping lines that are "standalone" helpers (only a block helper, comment, or partial and whitespace).

210 |
{{#each nav}}
211 |   <a href="{{url}}">
212 |     {{#if test}}
213 |       {{title}}
214 |     {{else}}
215 |       Empty
216 |     {{/if}}
217 |   </a>
218 | {{~/each}}
219 | 
220 |

渲染结果:

221 |
<a href="foo">
222 |     bar
223 | </a>
224 | <a href="bar">
225 |     Empty
226 | </a>
227 | 
228 |

示例

229 |
230 | 231 |

Id Tracking

232 | 233 | 234 |
235 |

Optionally, helpers can be informed of the paths that were used to lookup an argument for a given value. This mode may be enabled via the trackIds compiler flag.

236 |
{{foo bar.baz}}
237 | 

would call the helper foo with the value of bar.baz but also will include the literal string "bar.baz" in the ids field on the options argument.

238 |

This can be used for future lookup of parameters should it be necessary, but does add additional overhead.

239 |

When this mode is enabled, all builtin helpers will generate a @contextPath variable that denotes the lookup path for the current context. It's highly recommended that generic helpers provide such a variable if they modify the context when executing their children.

240 |
241 | 242 | 243 | 发现翻译错误?告诉我们。 244 | 245 |
246 |
247 |
248 | 249 | Fork me on GitHub 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /static/js/handlebars.runtime-v3.0.1.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | handlebars v3.0.1 4 | 5 | Copyright (C) 2011-2014 by Yehuda Katz 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | @license 26 | */ 27 | /* exported Handlebars */ 28 | (function (root, factory) { 29 | if (typeof define === 'function' && define.amd) { 30 | define([], factory); 31 | } else if (typeof exports === 'object') { 32 | module.exports = factory(); 33 | } else { 34 | root.Handlebars = factory(); 35 | } 36 | }(this, function () { 37 | // handlebars/utils.js 38 | var __module2__ = (function() { 39 | "use strict"; 40 | var __exports__ = {}; 41 | /*jshint -W004 */ 42 | var escape = { 43 | "&": "&", 44 | "<": "<", 45 | ">": ">", 46 | '"': """, 47 | "'": "'", 48 | "`": "`" 49 | }; 50 | 51 | var badChars = /[&<>"'`]/g; 52 | var possible = /[&<>"'`]/; 53 | 54 | function escapeChar(chr) { 55 | return escape[chr]; 56 | } 57 | 58 | function extend(obj /* , ...source */) { 59 | for (var i = 1; i < arguments.length; i++) { 60 | for (var key in arguments[i]) { 61 | if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { 62 | obj[key] = arguments[i][key]; 63 | } 64 | } 65 | } 66 | 67 | return obj; 68 | } 69 | 70 | __exports__.extend = extend;var toString = Object.prototype.toString; 71 | __exports__.toString = toString; 72 | // Sourced from lodash 73 | // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt 74 | var isFunction = function(value) { 75 | return typeof value === 'function'; 76 | }; 77 | // fallback for older versions of Chrome and Safari 78 | /* istanbul ignore next */ 79 | if (isFunction(/x/)) { 80 | isFunction = function(value) { 81 | return typeof value === 'function' && toString.call(value) === '[object Function]'; 82 | }; 83 | } 84 | var isFunction; 85 | __exports__.isFunction = isFunction; 86 | /* istanbul ignore next */ 87 | var isArray = Array.isArray || function(value) { 88 | return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; 89 | }; 90 | __exports__.isArray = isArray; 91 | // Older IE versions do not directly support indexOf so we must implement our own, sadly. 92 | function indexOf(array, value) { 93 | for (var i = 0, len = array.length; i < len; i++) { 94 | if (array[i] === value) { 95 | return i; 96 | } 97 | } 98 | return -1; 99 | } 100 | 101 | __exports__.indexOf = indexOf; 102 | function escapeExpression(string) { 103 | if (typeof string !== 'string') { 104 | // don't escape SafeStrings, since they're already safe 105 | if (string && string.toHTML) { 106 | return string.toHTML(); 107 | } else if (string == null) { 108 | return ''; 109 | } else if (!string) { 110 | return string + ''; 111 | } 112 | 113 | // Force a string conversion as this will be done by the append regardless and 114 | // the regex test will do this transparently behind the scenes, causing issues if 115 | // an object's to string has escaped characters in it. 116 | string = '' + string; 117 | } 118 | 119 | if (!possible.test(string)) { return string; } 120 | return string.replace(badChars, escapeChar); 121 | } 122 | 123 | __exports__.escapeExpression = escapeExpression;function isEmpty(value) { 124 | if (!value && value !== 0) { 125 | return true; 126 | } else if (isArray(value) && value.length === 0) { 127 | return true; 128 | } else { 129 | return false; 130 | } 131 | } 132 | 133 | __exports__.isEmpty = isEmpty;function blockParams(params, ids) { 134 | params.path = ids; 135 | return params; 136 | } 137 | 138 | __exports__.blockParams = blockParams;function appendContextPath(contextPath, id) { 139 | return (contextPath ? contextPath + '.' : '') + id; 140 | } 141 | 142 | __exports__.appendContextPath = appendContextPath; 143 | return __exports__; 144 | })(); 145 | 146 | // handlebars/exception.js 147 | var __module3__ = (function() { 148 | "use strict"; 149 | var __exports__; 150 | 151 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; 152 | 153 | function Exception(message, node) { 154 | var loc = node && node.loc, 155 | line, 156 | column; 157 | if (loc) { 158 | line = loc.start.line; 159 | column = loc.start.column; 160 | 161 | message += ' - ' + line + ':' + column; 162 | } 163 | 164 | var tmp = Error.prototype.constructor.call(this, message); 165 | 166 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. 167 | for (var idx = 0; idx < errorProps.length; idx++) { 168 | this[errorProps[idx]] = tmp[errorProps[idx]]; 169 | } 170 | 171 | if (loc) { 172 | this.lineNumber = line; 173 | this.column = column; 174 | } 175 | } 176 | 177 | Exception.prototype = new Error(); 178 | 179 | __exports__ = Exception; 180 | return __exports__; 181 | })(); 182 | 183 | // handlebars/base.js 184 | var __module1__ = (function(__dependency1__, __dependency2__) { 185 | "use strict"; 186 | var __exports__ = {}; 187 | var Utils = __dependency1__; 188 | var Exception = __dependency2__; 189 | 190 | var VERSION = "3.0.1"; 191 | __exports__.VERSION = VERSION;var COMPILER_REVISION = 6; 192 | __exports__.COMPILER_REVISION = COMPILER_REVISION; 193 | var REVISION_CHANGES = { 194 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 195 | 2: '== 1.0.0-rc.3', 196 | 3: '== 1.0.0-rc.4', 197 | 4: '== 1.x.x', 198 | 5: '== 2.0.0-alpha.x', 199 | 6: '>= 2.0.0-beta.1' 200 | }; 201 | __exports__.REVISION_CHANGES = REVISION_CHANGES; 202 | var isArray = Utils.isArray, 203 | isFunction = Utils.isFunction, 204 | toString = Utils.toString, 205 | objectType = '[object Object]'; 206 | 207 | function HandlebarsEnvironment(helpers, partials) { 208 | this.helpers = helpers || {}; 209 | this.partials = partials || {}; 210 | 211 | registerDefaultHelpers(this); 212 | } 213 | 214 | __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { 215 | constructor: HandlebarsEnvironment, 216 | 217 | logger: logger, 218 | log: log, 219 | 220 | registerHelper: function(name, fn) { 221 | if (toString.call(name) === objectType) { 222 | if (fn) { throw new Exception('Arg not supported with multiple helpers'); } 223 | Utils.extend(this.helpers, name); 224 | } else { 225 | this.helpers[name] = fn; 226 | } 227 | }, 228 | unregisterHelper: function(name) { 229 | delete this.helpers[name]; 230 | }, 231 | 232 | registerPartial: function(name, partial) { 233 | if (toString.call(name) === objectType) { 234 | Utils.extend(this.partials, name); 235 | } else { 236 | if (typeof partial === 'undefined') { 237 | throw new Exception('Attempting to register a partial as undefined'); 238 | } 239 | this.partials[name] = partial; 240 | } 241 | }, 242 | unregisterPartial: function(name) { 243 | delete this.partials[name]; 244 | } 245 | }; 246 | 247 | function registerDefaultHelpers(instance) { 248 | instance.registerHelper('helperMissing', function(/* [args, ]options */) { 249 | if(arguments.length === 1) { 250 | // A missing field in a {{foo}} constuct. 251 | return undefined; 252 | } else { 253 | // Someone is actually trying to call something, blow up. 254 | throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'"); 255 | } 256 | }); 257 | 258 | instance.registerHelper('blockHelperMissing', function(context, options) { 259 | var inverse = options.inverse, 260 | fn = options.fn; 261 | 262 | if(context === true) { 263 | return fn(this); 264 | } else if(context === false || context == null) { 265 | return inverse(this); 266 | } else if (isArray(context)) { 267 | if(context.length > 0) { 268 | if (options.ids) { 269 | options.ids = [options.name]; 270 | } 271 | 272 | return instance.helpers.each(context, options); 273 | } else { 274 | return inverse(this); 275 | } 276 | } else { 277 | if (options.data && options.ids) { 278 | var data = createFrame(options.data); 279 | data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name); 280 | options = {data: data}; 281 | } 282 | 283 | return fn(context, options); 284 | } 285 | }); 286 | 287 | instance.registerHelper('each', function(context, options) { 288 | if (!options) { 289 | throw new Exception('Must pass iterator to #each'); 290 | } 291 | 292 | var fn = options.fn, inverse = options.inverse; 293 | var i = 0, ret = "", data; 294 | 295 | var contextPath; 296 | if (options.data && options.ids) { 297 | contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; 298 | } 299 | 300 | if (isFunction(context)) { context = context.call(this); } 301 | 302 | if (options.data) { 303 | data = createFrame(options.data); 304 | } 305 | 306 | function execIteration(key, i, last) { 307 | if (data) { 308 | data.key = key; 309 | data.index = i; 310 | data.first = i === 0; 311 | data.last = !!last; 312 | 313 | if (contextPath) { 314 | data.contextPath = contextPath + key; 315 | } 316 | } 317 | 318 | ret = ret + fn(context[key], { 319 | data: data, 320 | blockParams: Utils.blockParams([context[key], key], [contextPath + key, null]) 321 | }); 322 | } 323 | 324 | if(context && typeof context === 'object') { 325 | if (isArray(context)) { 326 | for(var j = context.length; i 2 | 3 | 4 | Handlebars 中文网:轻逻辑语义化的模板引擎 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 | 20 | Buy Handlebars swag on DevSwag! 21 | 22 | 23 |
24 | 25 |

你可以使用 Handlebars 轻松的创建语义化模板。
Handlebars 兼容 Mustache 模板。你可以在 Handlebars 中直接使用 Mustache 模板。

26 |

27 | Download: 3.0.1 28 | Download: runtime-3.0.1

29 |

快速入门

30 | 31 |

Handlebars 模板看起来很像 HTML ,Handlebars 表达式嵌入在 HTML 中。

32 |
<div class="entry">
 33 |   <h1>{{title}}</h1>
 34 |   <div class="body">
 35 |     {{body}}
 36 |   </div>
 37 | </div>
 38 | 
39 |

Handlebars 表达式以 {{属性名}} 的方式插入数据。

40 |

高级教程:表达式

41 |
42 |

你可以在 HTML 中使用 <script> 标签存放模板

43 |
<script id="entry-template" type="text/x-handlebars-template">
 44 |   <div class="entry">
 45 |     <h1>{{title}}</h1>
 46 |     <div class="body">
 47 |       {{body}}
 48 |     </div>
 49 |   </div>
 50 | </script>
 51 | 
52 |
53 |

在 JavaScript 中获取 <scrit> 中的模板并编译模板

54 |
var source   = $("#entry-template").html();
 55 | var template = Handlebars.compile(source);
 56 | 
57 |
58 |

建议初学者跳过预编译章节 59 |

60 |

还可以预编译你的模板。预编译后的模板只需使用 handlebars.runtime.js 渲染,这样可以提高性能并减少文件大小。在移动设备中这样做非常有意义(因为移动设备的性能和网络状态都没有PC好)。

61 |

高级教程:预编译

62 |
63 |

传入数据,执行 Handlebars 返回 渲染后的 HTML。

64 |
var source   = $("#entry-template").html();
 65 | var template = Handlebars.compile(source);
 66 | 
 67 | var context = {title: "My New Post", body: "This is my first post!"};
 68 | var html    = template(context);
 69 | 
70 |

渲染结果

71 |
<div class="entry">
 72 |   <h1>My New Post</h1>
 73 |   <div class="body">
 74 |     This is my first post!
 75 |   </div>
 76 | </div>
 77 | 
78 |

示例

79 |

高级教程: 执行

80 |

HTML 转义

81 |

遇到 HTML标签时 Handlebars 会返回转义后的 HTML,如果你不希望被转义,可以使用 {{{

82 |
<div class="entry">
 83 |   <h1>{{title}}</h1>
 84 |   <div class="body">
 85 |     {{{body}}}
 86 |   </div>
 87 | </div>
 88 | 
89 |

渲染数据:

90 |
{
 91 |   title: "All about <p> Tags",
 92 |   body: "<p>This is a post about &lt;p&gt; tags</p>"
 93 | }
 94 | 
95 |

渲染结果:

96 |
<div class="entry">
 97 |   <h1>All About &lt;p&gt; Tags</h1>
 98 |   <div class="body">
 99 |     <p>This is a post about &lt;p&gt; tags</p>
100 |   </div>
101 | </div>
102 | 
103 |

104 |

Handlebars 不会转义 Handlebars.SafeString 。如果你自定义了一个 helper 返回 HTML 代码,你需要返回 new Handlebars.SafeString(result) ,那么你需要手动对内容进行转义

105 |
Handlebars.registerHelper('link', function(text, url) {
106 |   text = Handlebars.Utils.escapeExpression(text);
107 |   url  = Handlebars.Utils.escapeExpression(url);
108 | 
109 |   var result = '<a href="' + url + '">' + text + '</a>';
110 | 
111 |   return new Handlebars.SafeString(result);
112 | });
113 | 
114 |

new Handlebars.SafeString() 会标识传入参数是“安全的”,所以即使你不使用 {{{ 。Handlebars 也不会转义。

115 |

示例

116 |

块表达式

117 |

块表达式允许你定义helper,用不同的数据上下文(context)调用一段模板。下面我们定义一个生成列表的helper:

118 |

快表达式允许你自定义 helper,使用当前传入参数作为上下文调用模板。

119 |

创建一个用于生产列表的快表达式

120 |
{{#list people}}{{firstName}} {{lastName}}{{/list}}
121 | 

渲染数据如下所示:

122 |
{
123 |   people: [
124 |     {firstName: "Yehuda", lastName: "Katz"},
125 |     {firstName: "Carl", lastName: "Lerche"},
126 |     {firstName: "Alan", lastName: "Johnson"}
127 |   ]
128 | }
129 | 
130 |

we would create a helper named list to generate our HTML list. The helper receives the people as its first parameter, and an options hash as its second parameter. The options hash contains a property named fn, which you can invoke with a context just as you would invoke a normal Handlebars template.

131 |
Handlebars.registerHelper('list', function(items, options) {
132 |   var out = "<ul>";
133 | 
134 |   for(var i=0, l=items.length; i<l; i++) {
135 |     out = out + "<li>" + options.fn(items[i]) + "</li>";
136 |   }
137 | 
138 |   return out + "</ul>";
139 | });
140 | 
141 |

When executed, the template will render:

142 |
<ul>
143 |   <li>Yehuda Katz</li>
144 |   <li>Carl Lerche</li>
145 |   <li>Alan Johnson</li>
146 | </ul>
147 | 
148 |

Block helpers have more features, such as the ability to create an else section (used, for instance, by the built-in if helper).

149 |

Since the contents of a block helper are escaped when you call options.fn(context), Handlebars does not escape the results of a block helper. If it did, inner content would be double-escaped!

150 |

Learn More: Block Helpers

151 |

Handlebars Paths

152 |

Handlebars supports simple paths, just like Mustache.

153 |
<p>{{name}}</p>
154 | 
155 |

Handlebars also supports nested paths, making it possible to look up properties nested below the current context.

156 |
<div class="entry">
157 |   <h1>{{title}}</h1>
158 |   <h2>By {{author.name}}</h2>
159 | 
160 |   <div class="body">
161 |     {{body}}
162 |   </div>
163 | </div>
164 | 
165 |

That template works with this context

166 |
var context = {
167 |   title: "My First Blog Post!",
168 |   author: {
169 |     id: 47,
170 |     name: "Yehuda Katz"
171 |   },
172 |   body: "My first post. Wheeeee!"
173 | };
174 | 

This makes it possible to use Handlebars templates with more raw JSON objects.

175 |

Nested handlebars paths can also include ../ segments, which evaluate their paths against a parent context.

176 |
<h1>Comments</h1>
177 | 
178 | <div id="comments">
179 |   {{#each comments}}
180 |   <h2><a href="/posts/{{../permalink}}#{{id}}">{{title}}</a></h2>
181 |   <div>{{body}}</div>
182 |   {{/each}}
183 | </div>
184 | 
185 |

Even though the link is printed while in the context of a comment, it can still go back to the main context (the post) to retrieve its permalink.

186 |

The ../ path segment references the parent template scope, not one level up in the context. This is because block helpers can invoke a block with any context, so the notion of "one level up" isn't particularly meaningful except as a reference to the parent template scope.

187 |

Handlebars also allows for name conflict resolution between helpers and data fields via a this reference:

188 |
<p>{{./name}} or {{this/name}} or {{this.name}}</p>
189 | 
190 |

Any of the above would cause the name field on the current context to be used rather than a helper of the same name.

191 |

Template comments with {{!-- --}} or {{! }}.

192 |

You can use comments in your handlebars code just as you would in your code. Since there is generally some level of logic, this is a good practice.

193 |
<div class="entry">
194 |   {{!-- only output this author names if an author exists --}}
195 |   {{#if author}}
196 |     <h1>{{firstName}} {{lastName}}</h1>
197 |   {{/if}}
198 | </div>
199 | 
200 |

The comments will not be in the resulting output. If you'd like the comments to show up. Just use html comments, and they will be output.

201 |
<div class="entry">
202 |   {{! This comment will not be in the output }}
203 |   <!-- This comment will be in the output -->
204 | </div>
205 | 
206 |

Any comments that must contain }} or other handlebars tokens should use the {{!-- --}} syntax.

207 |

Helpers

208 |

Handlebars helpers can be accessed from any context in a template. You can register a helper with the Handlebars.registerHelper method.

209 |
<div class="post">
210 |   <h1>By {{fullName author}}</h1>
211 |   <div class="body">{{body}}</div>
212 |   <h1>Comments</h1>
213 |   {{#each comments}}
214 |   <h2>By {{fullName author}}</h2>
215 |   <div class="body">{{body}}</div>
216 |   {{/each}}
217 | </div>
218 | 
219 |

when using this context and helpers:

220 |
var context = {
221 |   author: {firstName: "Alan", lastName: "Johnson"},
222 |   body: "I Love Handlebars",
223 |   comments: [{
224 |     author: {firstName: "Yehuda", lastName: "Katz"},
225 |     body: "Me too!"
226 |   }]
227 | };
228 | 
229 | Handlebars.registerHelper('fullName', function(person) {
230 |   return person.firstName + " " + person.lastName;
231 | });
232 | 
233 |

results in:

234 |
<div class="post">
235 |   <h1>By Alan Johnson</h1>
236 |   <div class="body">I Love Handlebars</div>
237 |   <h1>Comments</h1>
238 |   <h2>By Yehuda Katz</h2>
239 |   <div class="body">Me Too!</div>
240 | </div>
241 | 
242 |

Helpers receive the current context as the this context of the function.

243 |
<ul>
244 |   {{#each items}}
245 |   <li>{{agree_button}}</li>
246 |   {{/each}}
247 | </ul>
248 | 
249 |

when using this context and helpers:

250 |
var context = {
251 |   items: [
252 |     {name: "Handlebars", emotion: "love"},
253 |     {name: "Mustache", emotion: "enjoy"},
254 |     {name: "Ember", emotion: "want to learn"}
255 |   ]
256 | };
257 | 
258 | Handlebars.registerHelper('agree_button', function() {
259 |   var emotion = Handlebars.escapeExpression(this.emotion),
260 |       name = Handlebars.escapeExpression(this.name);
261 | 
262 |   return new Handlebars.SafeString(
263 |     "<button>I agree. I " + emotion + " " + name + "</button>"
264 |   );
265 | });
266 | 
267 |

results in:

268 |
<ul>
269 |   <li><button>I agree. I love Handlebars</button></li>
270 |   <li><button>I agree. I enjoy Mustache</button></li>
271 |   <li><button>I agree. I want to learn Ember</button></li>
272 | </ul>
273 | 
274 |

If your helper returns HTML that you do not want escaped, make sure to return a new Handlebars.SafeString.

275 |

Built-In Helpers

276 |

Handlebars offers a variety of built-in helpers such as the if conditional and each iterator.

277 |

Learn More: Built-In Helpers

278 |

API Reference

279 |

Handlebars offers a variety of APIs and utility methods for applications and helpers.

280 |

Learn More: API Reference

281 |

<!-- End .contents

282 | 283 | 284 | 发现翻译错误?告诉我们。 285 | 286 |
287 | 288 |
289 | 290 | Fork me on GitHub 291 | 292 | 293 | 294 | --------------------------------------------------------------------------------