Ember模板语法 #

一、条件渲染 #

1.1 if/else #

handlebars
{{! 基本if}}
{{#if this.isLoggedIn}}
  <p>欢迎回来!</p>
{{/if}}

{{! if/else}}
{{#if this.isLoggedIn}}
  <p>欢迎回来!</p>
{{else}}
  <p>请先登录</p>
{{/if}}

{{! if/else if/else}}
{{#if this.isAdmin}}
  <p>管理员面板</p>
{{else if this.isEditor}}
  <p>编辑者面板</p>
{{else}}
  <p>用户面板</p>
{{/if}}

1.2 unless #

handlebars
{{! unless - if的反向}}
{{#unless this.isPublished}}
  <span class="draft">草稿</span>
{{/unless}}

{{! 等价于}}
{{#if (not this.isPublished)}}
  <span class="draft">草稿</span>
{{/if}}

1.3 内联if #

handlebars
{{! 内联if助手}}
<span class="status">{{if this.isActive "激活" "未激活"}}</span>

{{! 只有两个参数时}}
<span>{{if this.hasError "错误"}}</span>

1.4 复杂条件 #

handlebars
{{! 使用and/or/not助手}}
{{#if (and this.isLoggedIn this.hasPermission)}}
  <p>可以访问</p>
{{/if}}

{{#if (or this.isAdmin this.isEditor)}}
  <p>管理权限</p>
{{/if}}

{{#if (not this.isBanned)}}
  <p>正常用户</p>
{{/if}}

{{! 使用eq助手}}
{{#if (eq this.status "active")}}
  <span class="active">活跃</span>
{{/if}}

二、循环渲染 #

2.1 each基本用法 #

handlebars
<ul>
  {{#each @model as |post|}}
    <li>{{post.title}}</li>
  {{/each}}
</ul>

2.2 each with index #

handlebars
<ol>
  {{#each @model as |post index|}}
    <li>
      <span class="number">{{index}}</span>
      {{post.title}}
    </li>
  {{/each}}
</ol>

2.3 each with else #

handlebars
<ul>
  {{#each @model as |post|}}
    <li>{{post.title}}</li>
  {{else}}
    <li class="empty">暂无文章</li>
  {{/each}}
</ul>

2.4 each-in #

handlebars
{{! 遍历对象}}
<dl>
  {{#each-in this.user as |key value|}}
    <dt>{{key}}</dt>
    <dd>{{value}}</dd>
  {{/each-in}}
</dl>

2.5 嵌套循环 #

handlebars
{{#each @categories as |category|}}
  <div class="category">
    <h2>{{category.name}}</h2>
    <ul>
      {{#each category.items as |item|}}
        <li>{{item.name}}</li>
      {{/each}}
    </ul>
  </div>
{{/each}}

三、动态属性 #

3.1 动态class #

handlebars
{{! 条件class}}
<div class="card {{if this.isActive "active"}}">
  内容
</div>

{{! 多条件class}}
<div class="btn {{this.size}} {{this.type}} {{if this.disabled "disabled"}}">
  按钮
</div>

{{! 使用concat}}
<div class="{{concat "alert-" this.type}}">
  提示信息
</div>

3.2 动态属性 #

handlebars
{{! 动态属性值}}
<input type="text" value={{this.inputValue}} />

{{! 动态属性名}}
<div {{prop this.attrName this.attrValue}}></div>

{{! 布尔属性}}
<input type="checkbox" checked={{this.isChecked}} />
<button disabled={{this.isDisabled}}>提交</button>

3.3 动态style #

handlebars
{{! 内联style}}
<div style="color: {{this.color}}; background: {{this.bgColor}}">
  内容
</div>

{{! 使用html-safe}}
<div style={{this.computedStyle}}>内容</div>
javascript
import { htmlSafe } from '@ember/template';

get computedStyle() {
  return htmlSafe(`color: ${this.color}; font-size: ${this.fontSize}px`);
}

四、模板组合 #

4.1 yield产出 #

handlebars
{{! 父模板}}
<Card @title="用户信息">
  <p>姓名:{{@user.name}}</p>
  <p>邮箱:{{@user.email}}</p>
</Card>

{{! Card组件}}
<div class="card">
  <div class="card-header">
    <h3>{{@title}}</h3>
  </div>
  <div class="card-body">
    {{yield}}
  </div>
</div>

4.2 块参数 #

handlebars
{{! 父模板}}
<DataTable @data={{@users} as |row|>
  <td>{{row.name}}</td>
  <td>{{row.email}}</td>
  <td>{{row.role}}</td>
</DataTable>

{{! DataTable组件}}
<table>
  <tbody>
    {{#each @data as |row|}}
      <tr>
        {{yield row}}
      </tr>
    {{/each}}
  </tbody>
</table>

4.3 多个yield #

handlebars
{{! 父模板}}
<Layout>
  <:header>
    <h1>页面标题</h1>
  </:header>
  
  <:main>
    <p>主要内容</p>
  </:main>
  
  <:footer>
    <p>页脚信息</p>
  </:footer>
</Layout>

{{! Layout组件}}
<div class="layout">
  <header class="header">
    {{yield to="header"}}
  </header>
  <main class="main">
    {{yield to="main"}}
  </main>
  <footer class="footer">
    {{yield to="footer"}}
  </footer>
</div>

五、模板引用 #

5.1 基本引用 #

handlebars
<div {{ref "myDiv"}}>
  内容
</div>

<button {{on "click" (fn this.scrollTo "myDiv")}}>
  滚动到内容
</button>
javascript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class MyComponent extends Component {
  @tracked refs = {};

  @action
  scrollTo(refName) {
    const element = this.refs[refName];
    element?.scrollIntoView({ behavior: 'smooth' });
  }
}

六、模板助手 #

6.1 内置助手 #

handlebars
{{! 数组助手}}
{{#each (array "a" "b" "c") as |item|}}
  {{item}}
{{/each}}

{{! 对象助手}}
{{#let (hash name="张三" age=25) as |user|}}
  {{user.name}}
{{/let}}

{{! 字符串拼接}}
{{concat "Hello" " " "World"}}

{{! 动态获取}}
{{get this.user this.fieldName}}

6.2 数学助手 #

handlebars
{{! 加法}}
{{add 1 2}}

{{! 减法}}
{{sub 10 3}}

{{! 乘法}}
{{mul 4 5}}

{{! 除法}}
{{div 20 4}}

{{! 取模}}
{{mod 10 3}}

6.3 比较助手 #

handlebars
{{! 等于}}
{{eq this.value "expected"}}

{{! 不等于}}
{{not-eq this.value "unexpected"}}

{{! 大于/小于}}
{{gt this.age 18}}
{{gte this.age 18}}
{{lt this.age 60}}
{{lte this.age 60}}

七、表单处理 #

7.1 Input组件 #

handlebars
{{! 文本输入}}
<Input
  @type="text"
  @value={{this.name}}
  placeholder="请输入姓名"
  {{on "input" this.validateName}}
/>

{{! 密码输入}}
<Input @type="password" @value={{this.password}} />

{{! 邮箱输入}}
<Input @type="email" @value={{this.email}} />

{{! 数字输入}}
<Input @type="number" @value={{this.age}} min="0" max="150" />

7.2 复选框和单选 #

handlebars
{{! 复选框}}
<label>
  <Input @type="checkbox" @checked={{this.agree}} />
  同意条款
</label>

{{! 单选按钮组}}
{{#each this.options as |option|}}
  <label>
    <Input
      @type="radio"
      @name="gender"
      @value={{option.value}}
      @checked={{eq this.selected option.value}}
    />
    {{option.label}}
  </label>
{{/each}}

7.3 选择框 #

handlebars
<select {{on "change" this.handleChange}}>
  <option value="">请选择</option>
  {{#each this.options as |option|}}
    <option value={{option.value}} selected={{eq this.selected option.value}}>
      {{option.label}}
    </option>
  {{/each}}
</select>

7.4 Textarea #

handlebars
<Textarea
  @value={{this.content}}
  rows="5"
  placeholder="请输入内容"
  {{on "input" this.countCharacters}}
/>
<p>已输入 {{this.charCount}} 字符</p>

八、链接与导航 #

8.1 LinkTo #

handlebars
{{! 基本链接}}
<LinkTo @route="home">首页</LinkTo>

{{! 带模型参数}}
<LinkTo @route="posts.show" @model={{@post.id}}>
  查看文章
</LinkTo>

{{! 带查询参数}}
<LinkTo @route="posts" @query={{hash page=1 sort="date"}}>
  文章列表
</LinkTo>

{{! 禁用链接}}
<LinkTo @route="home" @disabled={{true}}>禁用链接</LinkTo>

{{! 替换历史记录}}
<LinkTo @route="home" @replace={{true}}>首页</LinkTo>

8.2 动态链接 #

handlebars
{{! 动态路由名}}
<LinkTo @route={{this.currentRoute}}>当前页面</LinkTo>

{{! 条件链接}}
{{#if this.canEdit}}
  <LinkTo @route="posts.edit" @model={{@post.id}}>编辑</LinkTo>
{{else}}
  <span class="disabled">无权限编辑</span>
{{/if}}

九、异步模板 #

9.1 处理Promise #

handlebars
{{! Ember自动处理Promise}}
{{#each @model as |post|}}
  {{! 数据加载完成后渲染}}
  <p>{{post.title}}</p>
{{/each}}

9.2 加载状态 #

handlebars
{{#if this.isLoading}}
  <div class="loading">加载中...</div>
{{else}}
  {{! 显示内容}}
{{/if}}

十、模板调试 #

10.1 log助手 #

handlebars
{{! 输出到控制台}}
{{log "调试信息:" this.user}}

{{! 输出多个值}}
{{log "用户:" this.user "状态:" this.status}}

10.2 debugger #

handlebars
{{! 断点调试}}
{{debugger}}

{{! 条件断点}}
{{#if this.hasError}}
  {{debugger}}
{{/if}}

十一、模板性能优化 #

11.1 避免重复计算 #

handlebars
{{! 不推荐 - 重复计算}}
<p>{{format-date @post.createdAt}}</p>
<small>{{format-date @post.createdAt}}</small>

{{! 推荐 - 使用let}}
{{#let (format-date @post.createdAt) as |date|}}
  <p>{{date}}</p>
  <small>{{date}}</small>
{{/let}}

11.2 合理使用组件 #

handlebars
{{! 不推荐 - 复杂模板}}
<ul>
  {{#each @items as |item|}}
    <li class="item {{if item.active "active"}}">
      <span class="name">{{item.name}}</span>
      <span class="price">{{item.price}}</span>
      {{! 更多复杂结构...}}
    </li>
  {{/each}}
</ul>

{{! 推荐 - 组件化}}
<ul>
  {{#each @items as |item|}}
    <ItemRow @item={{item}} />
  {{/each}}
</ul>

十二、总结 #

Ember模板语法要点:

语法 用途
{{#if}} 条件渲染
{{#each}} 循环渲染
{{on}} 事件绑定
{{yield}} 内容产出
<LinkTo> 路由链接
<Input> 表单输入
{{let}} 变量定义
{{log}} 调试输出

掌握这些模板语法,可以构建灵活、高效的Ember应用界面。

最后更新:2026-03-28