30、Ember.js模板开发:Handlebars的强大应用

Ember.js模板开发:Handlebars的强大应用

1. Handlebars基础

Handlebars是一种强大的动态模板创建语言,类似于PHP、JSP、ASP和ERB等服务器端模板语言。它包含HTML元素标签和分隔符来处理数据对象,分隔符为双花括号 {{}} 。在双花括号内,可以渲染数据字符串并使用辅助方法执行有限的逻辑,例如 {{outlet}} {{#each}}

Ember对Handlebars的实现最近过渡到了名为“HTMLBars”的新机制,本章中的一些语言细节来自HTMLBars,但也适用于较旧版本的Ember(至少可以追溯到1.13.x版本)。

2. 模型(Models)

在Ember中,模板总是由模型支持。这意味着在将模板渲染为HTML字符串并附加到DOM时,会将一个对象(或对象数组)作为参数传递。模型对象可以具有字符串、数组或其他对象的属性,在编写Ember和Handlebars模板时,使用双花括号访问该对象。例如,要显示模型属性的值,可以使用 {{model.name}} ,其中 name 是模型对象上的属性。

3. 辅助方法(Helpers)

Handlebars模板是由JavaScript函数插值的字符串。当函数遇到双花括号时,它会尝试用对象属性解析分隔符的实例,或调用嵌套函数返回字符串,这些嵌套函数称为辅助方法,在应用程序中创建。Handlebars库内置了一些辅助方法,Ember也添加了一些自己的辅助方法。

辅助方法有两种形式:
- 内联辅助方法 :使用语法 {{[辅助方法名称] [参数]}} ,参数可以包括选项哈希。例如:

{{input type="text" value=firstName disabled=entryNotAllowed size="50"}}
  • 块辅助方法 :使用块语法:
{{#[辅助方法名称] [参数]}}
  [块内容]
{{/[辅助方法名称]}}

例如,只向未登录的用户显示登录链接:

{{#if notSignedIn}}
  <a href="/">Sign In</a>
{{/if}}
4. 条件语句(Conditionals)

条件语句允许在Handlebars模板中引入基本的控制流,语法如下:

{{#if argument}}
  [渲染块内容]
{{else}}
  [渲染其他内容]
{{/if}}

或者:

{{#unless argument}}
  [渲染块内容]
{{/unless}}

条件语句接受一个解析为真值或假值的参数。真值是在布尔上下文中计算为 true 的值,除了定义为假值的值( false 0 "" null undefined NaN )之外,所有值都是真值。

操作步骤:
1. 打开 app/templates/sightings/index.hbs 文件,添加条件语句,使目击记录显示位置信息,或者在没有位置数据时显示缺失数据的提示:

<div class="panel panel-default">
  <ul class="list-group">
    {{#each model as |sighting|}}
      <li class="list-group-item">
{{sighting.location}} - {{sighting.sightedAt}}
      </li>
    {{/each}}
  </ul>
</div>
<div class="row">
  {{#each model as |sighting|}}
    <div class="col-xs-12 col-sm-3 text-center">
      <div class="media well">
<div class="caption">
{{#if sighting.location}}
<h3>{{sighting.location}} - {{sighting.sightedAt}}</h3>
{{else}}
<h3 class="text-danger">Bogus Sighting</h3>
{{/if}}
</div>
      </div>
    </div>
  {{/each}}
</div>
  1. 更改 app/routes/sightings.js 中路由发送到模板的数据,将一个目击记录的 location 属性设置为空字符串:
model(){
  ...
  let record3 = this.store.createRecord('sighting', {
    location: 'Asilomar',
    sightedAt: new Date('2016-03-21')
  });
  return [record1, record2, record3];
}
  1. 启动服务器,在浏览器中访问 http://localhost:4200/sightings 查看新的目击记录列表。
5. 使用 {{#each}} 进行循环

{{#each}} 块辅助方法用于渲染数组中的每个对象,作为块内容的实例。 {{#each}} 的参数是数组,块内使用 |实例| 表示当前对象。只有当传递给 {{#each}} 的参数是至少有一个元素的数组时,块才会渲染。

{{#if}} 块辅助方法类似, {{#each}} 支持 {{else}} 块,当数组参数为空时渲染。

操作步骤:
1. 在 app/templates/cryptids.hbs 中使用 {{#each}} {{else}} {{/each}} 创建所有记录的神秘生物列表,或者在没有记录时显示“没有生物”:

{{outlet}}
<div class="row">
  {{#each model as |cryptid|}}
    <div class="col-xs-12 col-sm-3 text-center">
      <div class="media well">
<div class="caption">
<h3>{{cryptid.name}}</h3>
</div>
      </div>
    </div>
  {{else}}
    <div class="jumbotron">
      <h1>No Creatures</h1>
    </div>
  {{/each}}
</div>
  1. 导航到 http://localhost:4200/cryptids 查看新的神秘生物列表。
  2. app/routes/cryptids.js 中注释掉返回语句,移除模型回调,以测试 {{else}} 块:
model(){
  // return this.store.findAll('cryptid');
}
  1. 重新加载 http://localhost:4200/cryptids ,此时神秘生物列表应为空白。
6. 绑定元素属性(Binding element attributes)

元素属性值可以像在DOM元素标签之间渲染元素内容一样,从控制器属性渲染。在早期版本的Ember中,有一个名为 {{bind-attr}} 的辅助方法用于绑定属性,现在由于HTMLBars,可以直接使用 {{}} 将属性绑定到属性。

属性绑定在 class src 等元素属性中很常见。神秘生物的数据中有图像路径,可以将其 src 属性动态绑定到模型属性。

操作步骤:
1. 在 app/templates/cryptids.hbs 中为神秘生物列表添加图像:

<div class="row">
  {{#each model as |cryptid|}}
    <div class="col-xs-12 col-sm-3 text-center">
      <div class="media well">
<img class="media-object thumbnail" src="{{cryptid.profileImg}}"
alt="{{cryptid.name}}" width="100%" height="100%">
<div class="caption">
<h3>{{cryptid.name}}</h3>
</div>
      </div>
    </div>
  {{else}}
    <div class="jumbotron">
      <h1>No Creatures</h1>
    </div>
  {{/each}}
</div>
  1. 将神秘生物的图像从课程资源添加到 tracker/public/assets/image/cryptids 目录。
  2. app/templates/cryptids.hbs 中使用内联 {{if}} 辅助方法处理缺失的图像:
<div class="row">
  {{#each model as |cryptid|}}
    <div class="col-xs-12 col-sm-3 text-center">
      <div class="media well">
<img class="media-object thumbnail" src="{{cryptid.profileImg}}"
src="{{if cryptid.profileImg cryptid.profileImg
'assets/images/cryptids/blank_th.png'}}"
alt="{{cryptid.name}}" width="100%" height="100%">
<div class="caption">
...
  1. app/routes/cryptids.js beforeModel 钩子中创建一个没有图像路径的神秘生物:
import Ember from 'ember';
export default Ember.Route.extend({
  beforeModel(){
    this.store.createRecord('cryptid', {
      "name": "Charlie",
      "cryptidType": "unicorn"
    });
  },
  model(){
    return this.store.findAll('cryptid');
  }
});
  1. 重新加载 http://localhost:4200/cryptids 查看新的图像列表。
7. 链接(Links)

在基于浏览器的应用程序中,路由是独特的,Ember监听一些事件钩子来管理应用程序中的路由。因此,应该使用 {{#link-to}} 块辅助方法创建链接,该辅助方法将路由(用字符串表示)作为第一个参数创建锚元素。

操作步骤:
1. 在 app/templates/application.hbs 中更新主导航,使用 {{#link-to}} 辅助方法替换导航栏的测试链接:

<div class="collapse navbar-collapse" id="top-navbar-collapse">
  <ul class="nav navbar-nav">
    <li>
      <a href="#">Test Link</a>
    </li>
    <li>
      <a href="#">Test Link</a>
    </li>
    <li>
      {{#link-to 'sightings'}}Sightings{{/link-to}}
    </li>
    <li>
      {{#link-to 'cryptids'}}Cryptids{{/link-to}}
    </li>
    <li>
      {{#link-to 'witnesses'}}Witnesses{{/link-to}}
    </li>
  </ul>
</div>
  1. 重新加载应用程序并测试链接。
  2. app/templates/cryptids.hbs 中,使用 {{#link-to}} 辅助方法将神秘生物的图像链接到每个生物的单独页面:
<div class="media well">    
  {{#link-to 'cryptid' cryptid.id}}
    <img class="media-object thumbnail"
      src="{{if cryptid.profileImg cryptid.profileImg
      'assets/images/cryptids/blank_th.png'}}"
      alt="{{cryptid.name}}" width="100%" height="100%">
  {{/link-to}}
  <div class="caption">
    <h3>{{cryptid.name}}</h3>
  </div>
</div>
  1. 编辑 router.js ,让 CryptidRoute 知道要期待动态值:
Router.map(function() {
  this.route('sightings', function() {
    this.route('new');
  });
  this.route('sighting', function() {
    this.route('edit');
  });
  this.route('cryptids');
  this.route('cryptid', {path: 'cryptids/:cryptid_id'});
  this.route('witnesses');
  this.route('witness');
});
  1. 点击神秘生物的图像,应该会看到一个空白页面,这表明应用程序正在路由到 CryptidRoute
  2. 移除 app/routes/cryptids.js 中的 beforeModel 钩子,因为创建操作应该保留在创建新神秘生物的页面。
  3. app/routes/cryptid.js 中添加请求神秘生物数据的代码:
import Ember from 'ember';
export default Ember.Route.extend({
  model(params){
    return this.store.findRecord('cryptid', params.cryptid_id);
  }
});
  1. 编辑 app/templates/cryptid.hbs ,显示神秘生物的图像和名称:
{{outlet}}
<div class="container text-center">
  <img class="img-rounded" src="{{model.profileImg}}" alt="{{model.type}}">
  <h3>{{model.name}}</h3>
</div>
  1. 在浏览器中使用导航栏导航到“Cryptids”,然后点击一个神秘生物的图像查看其详细页面。
8. 自定义辅助方法(Custom Helpers)

目击记录的 sightedAt 日期属性显示为丑陋的原始日期字符串,为了更好地格式化日期,使用 moment 库。

操作步骤:
1. 从终端添加 moment 库:

bower install moment --save
  1. ember-cli-build.js 中使用 app.import moment 添加到供应商资产:
// Add assets to app with import
app.import(bootstrapPath + 'javascripts/bootstrap.js');
app.import('bower_components/moment/moment.js');
return app.toTree();
  1. 更改 ember-cli-build.js 后,停止Ember服务器( Control+C )并重新启动( ember server )。
  2. 从终端生成辅助方法模块:
ember g helper moment-from
  1. 打开生成的文件 app/helpers/moment-from.js ,创建新函数:
import Ember from 'ember';
export function momentFrom(params) {
  var time = window.moment(...params);
  var formatted = time.fromNow();
  return new Ember.Handlebars.SafeString(
    '<span class="text-primary">'
    + formatted + '</span>'
  );
}
export default Ember.Helper.helper(momentFrom);
  1. app/templates/sightings/index.hbs 模板中使用自定义辅助方法:
{{#if sighting.location}}
  <h3>{{sighting.location}} - {{sighting.sightedAt}}</h3>
  <p>{{moment-from sighting.sightedAt}}</p>
{{else}}
...
9. 挑战任务
  • 青铜挑战 :为 {{#link-to}} 辅助方法添加 title 属性,使链接具有悬停内容,标题应为目击记录的神秘生物名称。
  • 白银挑战 :审查 moment 文档,更改 {{moment-from}} 辅助方法的输出,将日期格式化为“Sunday May 31, 2016”。
  • 黄金挑战 :创建一个自定义辅助方法来显示神秘生物缩略图,通过将图像路径传递给辅助方法,在所有出现神秘生物图像的地方使用该辅助方法来清理代码。

通过以上步骤,你可以深入了解Ember.js中Handlebars模板的使用,包括模型、辅助方法、条件语句、循环、属性绑定、链接和自定义辅助方法等方面,为开发功能丰富的Web应用程序打下坚实的基础。

Ember.js模板开发:Handlebars的强大应用

10. 技术总结与流程图

在前面的内容中,我们详细介绍了Ember.js中Handlebars模板开发的多个方面,下面通过流程图和表格来总结关键步骤。

创建和使用自定义辅助方法流程

graph LR
    A[添加moment库] --> B[在ember - cli - build.js中导入]
    B --> C[重启服务器]
    C --> D[生成辅助方法模块]
    D --> E[编写辅助方法函数]
    E --> F[在模板中使用辅助方法]

各项功能操作步骤总结表格
| 功能 | 操作步骤 |
| — | — |
| 条件语句使用 | 1. 打开 app/templates/sightings/index.hbs 添加条件语句;2. 更改 app/routes/sightings.js 中数据;3. 启动服务器查看结果 |
| {{#each}} 循环使用 | 1. 在 app/templates/cryptids.hbs 中创建列表;2. 导航查看列表;3. 移除模型回调测试 {{else}} 块 |
| 绑定元素属性 | 1. 在 app/templates/cryptids.hbs 添加图像;2. 复制图像到指定目录;3. 使用内联 {{if}} 处理缺失图像;4. 创建无图像路径生物;5. 重新加载页面查看结果 |
| 链接创建 | 1. 在 app/templates/application.hbs 更新导航;2. 测试链接;3. 在 app/templates/cryptids.hbs 添加图像链接;4. 编辑 router.js ;5. 移除 beforeModel 钩子;6. 添加请求数据代码;7. 编辑模板显示详细信息 |
| 自定义辅助方法 | 1. 添加 moment 库;2. 在 ember - cli - build.js 导入;3. 重启服务器;4. 生成辅助方法模块;5. 编写函数;6. 在模板中使用 |

11. 代码优化与注意事项

在实际开发中,除了按照步骤实现功能,还需要注意代码的优化和一些细节问题。

代码优化
- 减少重复代码 :使用自定义辅助方法可以减少模板中的重复逻辑,提高代码的可维护性。例如,在处理日期格式化时,将逻辑封装在 moment - from 辅助方法中,避免在多个地方重复编写日期处理代码。
- 合理使用条件语句和循环 :在使用 {{#if}} {{#each}} 时,确保逻辑清晰,避免嵌套过深导致代码难以理解。可以将复杂的逻辑拆分成多个辅助方法或组件来处理。

注意事项
- 路径问题 :在处理图像路径和文件导入时,要确保路径的正确性。特别是在部署应用程序时,需要注意实际文件的存储位置和路径配置。
- 数据加载 :在使用 model 钩子获取数据时,要考虑数据加载的性能和错误处理。例如,在 app/routes/cryptid.js 中,使用 findRecord 方法获取单个记录时,如果记录不存在或网络出现问题,需要进行相应的错误处理。

12. 实际应用案例分析

假设我们要开发一个完整的神秘生物目击记录应用程序,结合前面所学的知识,我们可以构建出一个功能丰富的界面。

  • 首页 :使用 {{#link-to}} 创建导航栏,链接到目击记录列表、神秘生物列表等页面。
  • 目击记录列表页 :使用 {{#each}} 循环显示所有目击记录,使用条件语句判断是否显示位置信息,使用自定义辅助方法格式化日期。
  • 神秘生物列表页 :使用 {{#each}} 显示神秘生物列表,绑定图像属性,处理缺失图像的情况,将图像链接到单个生物的详细页面。
  • 单个生物详细页 :通过动态路由参数获取生物信息,显示生物的图像和名称。

以下是一个简化的应用结构示例:

<!-- app/templates/application.hbs -->
<div class="collapse navbar-collapse" id="top-navbar-collapse">
  <ul class="nav navbar-nav">
    <li>{{#link-to 'sightings'}}Sightings{{/link-to}}</li>
    <li>{{#link-to 'cryptids'}}Cryptids{{/link-to}}</li>
  </ul>
</div>

<!-- app/templates/sightings/index.hbs -->
<div class="panel panel-default">
  <ul class="list-group">
    {{#each model as |sighting|}}
      <li class="list-group-item">
        {{#if sighting.location}}
          <h3>{{sighting.location}} - {{sighting.sightedAt}}</h3>
          <p>{{moment-from sighting.sightedAt}}</p>
        {{else}}
          <h3 class="text-danger">Bogus Sighting</h3>
        {{/if}}
      </li>
    {{/each}}
  </ul>
</div>

<!-- app/templates/cryptids.hbs -->
<div class="row">
  {{#each model as |cryptid|}}
    <div class="col-xs-12 col-sm-3 text-center">
      <div class="media well">
        {{#link-to 'cryptid' cryptid.id}}
          <img class="media-object thumbnail" src="{{if cryptid.profileImg cryptid.profileImg 'assets/images/cryptids/blank_th.png'}}" alt="{{cryptid.name}}" width="100%" height="100%">
        {{/link-to}}
        <div class="caption">
          <h3>{{cryptid.name}}</h3>
        </div>
      </div>
    </div>
  {{else}}
    <div class="jumbotron">
      <h1>No Creatures</h1>
    </div>
  {{/each}}
</div>

<!-- app/templates/cryptid.hbs -->
{{outlet}}
<div class="container text-center">
  <img class="img-rounded" src="{{model.profileImg}}" alt="{{model.type}}">
  <h3>{{model.name}}</h3>
</div>
13. 挑战任务实现思路
  • 青铜挑战 :为 {{#link-to}} 辅助方法添加 title 属性,只需要在使用 {{#link-to}} 的地方添加 title 参数即可。例如:
{{#link-to 'sightings' title=sighting.cryptid.name}}Sightings{{/link-to}}
  • 白银挑战 :要更改 {{moment - from}} 辅助方法的输出格式,需要修改 app/helpers/moment - from.js 中的函数。根据 moment 文档,使用合适的格式化方法。例如:
import Ember from 'ember';
export function momentFrom(params) {
  var time = window.moment(...params);
  var formatted = time.format('dddd MMMM D, YYYY');
  return new Ember.Handlebars.SafeString(
    '<span class="text-primary">'
    + formatted + '</span>'
  );
}
export default Ember.Helper.helper(momentFrom);
  • 黄金挑战 :创建自定义辅助方法显示神秘生物缩略图,首先生成辅助方法模块,然后编写函数处理图像路径。例如:
ember g helper cryptid-thumbnail
import Ember from 'ember';
export function cryptidThumbnail(params) {
  var imgPath = params[0]? params[0] : 'assets/images/cryptids/blank_th.png';
  return new Ember.Handlebars.SafeString(
    '<img class="media-object thumbnail" src="' + imgPath + '" alt="Cryptid" width="100%" height="100%">'
  );
}
export default Ember.Helper.helper(cryptidThumbnail);

在模板中使用:

<div class="media well">    
  {{#link-to 'cryptid' cryptid.id}}
    {{cryptid-thumbnail cryptid.profileImg}}
  {{/link-to}}
  <div class="caption">
    <h3>{{cryptid.name}}</h3>
  </div>
</div>

通过以上内容,我们全面掌握了Ember.js中Handlebars模板开发的核心技术,包括功能实现、代码优化、注意事项以及挑战任务的解决思路,能够开发出高质量的Web应用程序。

内容概要:本文介绍了一套针对智能穿戴设备的跑步/骑行轨迹记录系统实战方案,旨在解决传统运动APP存在的定位漂移、数据断层和路径分析单一等问题。系统基于北斗+GPS双模定位、惯性测量单元(IMU)和海拔传感器,实现高精度轨迹采集,并通过卡尔曼滤波算法修正定位误差,在信号弱环境下利用惯性导航补位,确保轨迹连续性。系统支持跑步与骑行两种场景的差异化功能,包括实时轨迹记录、多维度路径分析(如配速、坡度、能耗)、数据可视化(地图标注、曲线图、3D回放)、异常提醒及智能优化建议,并可通过蓝牙/Wi-Fi同步数据至手机APP,支持社交分享与专业软件导出。技术架构涵盖硬件层、设备端与手机端软件层以及云端数据存储,强调低功耗设计与用户体验优化。经过实测验证,系统在定位精度、续航能力和场景识别准确率方面均达到预期指标,具备良好的实用性和扩展性。; 适合人群:具备一定嵌入式开发或移动应用开发经验,熟悉物联网、传感器融合与数据可视化的技术人员,尤其是从事智能穿戴设备、运动健康类产品研发的工程师和产品经理;也适合高校相关专业学生作为项目实践参考。; 使用场景及目标:① 开发高精度运动轨迹记录功能,解决GPS漂移与断点问题;② 实现跑步与骑行场景下的差异化数据分析与个性化反馈;③ 构建完整的“终端采集-手机展示-云端存储”系统闭环,支持社交互动与商业拓展;④ 掌握低功耗优化、多源数据融合、动态功耗调节等关键技术在穿戴设备中的落地应用。; 阅读建议:此资源以真实项目为导向,不仅提供详细的技术实现路径,还包含硬件选型、测试验证与商业扩展思路,建议读者结合自身开发环境,逐步实现各模块功能,重点关注定位优化算法、功耗控制策略与跨平台数据同步机制的设计与调优。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值