创建简单组件
当用户查看我们的租借列表时,他们可能希望有一些交互式选项来帮助他们作出决定。让我们添加切换每个租赁图像大小的功能。为此,我们将使用一个组件。
我们生成一个rental-listing
组件来管理我们每个租赁的行为。每个组件名称中都要有一个破折号,来避免与可能的HTML元素冲突,因此命名rental-listing
允许,受但命名rental
不允许。
生成一个组件:
$ ember g component rental-listing
installing component
create app/components/rental-listing.js
create app/templates/components/rental-listing.hbs
installing component-test
create tests/integration/components/rental-listing-test.js
组件由两部分组成:
- 一个定义它外观的模板(
app/templates/components/rental-listing.hbs
) 一个JavaScript源文件(app/components/rental-listing.js
),用于定义它的行为方式。
我们的新rental-listing
组件将管理用户看到的样子和与租赁交互。首先,我们将租赁显示详细信息从rentals.hbs
模板中移到rental-listing.hbs
(app/templates/components/rental-listing.hbs
)并添加图像字段:
<article class="listing">
<img src="{ { rental.image } }" alt="">
<h3>{ { rental.title } }</h3>
<div class="detail owner">
<span>Owner:</span> { { rental.owner } }
</div>
<div class="detail type">
<span>Type:</span> { { rental.propertyType } }
</div>
<div class="detail location">
<span>Location:</span> { { rental.city } }
</div>
<div class="detail bedrooms">
<span>Number of bedrooms:</span> { { rental.bedrooms } }
</div>
</article>
对比看看原来的app/templates/rentals.hbs
,会发现基本上是把each model
循环中内容定义的内容移到了组件模板中。那么现在修改rentals.hbs
,在each model
循环中引用新建立的组件:
<div class="jumbo">
<div class="right tomster"></div>
<h2>Welcome!</h2>
<p>
We hope you find exactly what you're looking for in a place to stay.
</p>
{ { #link-to 'about' class="button" } }
About Us
{ { /link-to } }
</div>
{ { #each model as |rentalUnit| } }
{ { rental-listing rental=rentalUnit } }
{ { /each } }
这里我们调用名称为rental-listing
的组件,并将其rentalUnit
赋予组件的rental
属性。
用ember s
运行超级租赁应用,通过浏览器访问 http://c7302.ambari.apache.org:4200
。
隐藏和显示图片
现在添加按用户请求显示图片的功能。
我们使用{ { if } }
助手检查isWide
是否为true来决定是否显示租赁图像。我们还将添加一些文本来指示可以单击图像,并使用一个锚点元素包含它,给它一个image
类名,以便我们的测试可以找到它。
修改组件模板文件app/templates/components/rental-listing.hbs
:
<article class="listing">
<a class="image { { if isWide "wide" } }">
<img src="{ { rental.image } }" alt="">
<small>View Larger</small>
</a>
<h3>{ { rental.title } }</h3>
(下略)
isWide
的值来自于组件的JavaScript文件(app/components/rental-listing.js
)。由于我们希望以小图像开始,因此把属性设置为false
:
import Ember from 'ember';
export default Ember.Component.extend({
isWide: false
});
为了让用户可以放大图像,我们添加一个动作(action)来切换isWide
的值,我们定义这个动作叫toggleImageSize
。组件模板app/templates/components/rental-listing.hbs
修改为:
<article class="listing">
<a { { action 'toggleImageSize' } } class="image { { if isWide "wide" } }">
<img src="{ { rental.image } }" alt="">
<small>View Larger</small>
</a>
...
单击锚点元素将发送动作到组件。然后,Ember将进入actions
散列并调用toggleImageSize
函数。
动作散列是一个包含多个函数的组件对象。当用户与UI进行交互(例如点击)时,将调用这些函数。
我们创建toggleImageSize
函数并切换组件上的isWide
属性(app/components/rental-listing.js
):
import Ember from 'ember';
export default Ember.Component.extend({
isWide: false,
actions: {
toggleImageSize() {
this.toggleProperty('isWide');
}
}
});
现在,当我们在浏览器中点击图形或View Larger
链接时,会看到大图像显示。当我们再次点击放大的图像,我们看到它变小。
集成测试
Ember组件通常通过组件集成测试进行测试。组件集成测试在Ember渲染引擎的上下文中验证组件的行为。当在集成测试中运行时,组件将经历其常规渲染生命周期,访问依赖对象,并且通过Ember解析器加载。
我们的组件集成测试将测试两种不同的行为:
- 组件应显示有关租赁的详细信息
- 该组件应该在点击时切换
isWide
,以及扩展和缩小租赁照片。
对于测试,我们将会向组件传递具有租赁模型所有属性的假对象。我们给变量命名为rental
,在每个测试中,我们用this
对象,将rental
设置到本地范围(scope)。渲染模板可以访问本地范围内的值。 最终的tests/integration/components/rental-listing-test.js
:
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
let rental = Ember.Object.create({
image: 'fake.png',
title: 'test-title',
owner: 'test-owner',
propertyType: 'test-type',
city: 'test-city',
bedrooms: 3
});
moduleForComponent('rental-listing', 'Integration | Component | rental listing', {
integration: true
});
test('should display rental details', function(assert) {
this.set('rentalObj', rental);
this.render(hbs`{ { rental-listing rental=rentalObj } }`);
assert.equal(this.$('.listing h3').text(), 'test-title', 'Title: test-title');
assert.equal(this.$('.listing .owner').text().trim(), 'Owner: test-owner', 'Owner: test-owner')
});
test('should toggle wide class on click', function(assert) {
this.set('rentalObj', rental);
this.render(hbs`{ { rental-listing rental=rentalObj } }`);
assert.equal(this.$('.image.wide').length, 0, 'initially rendered small');
Ember.run(() => document.querySelector('.image').click());
assert.equal(this.$('.image.wide').length, 1, 'rendered wide after click');
Ember.run(() => document.querySelector('.image').click());
assert.equal(this.$('.image.wide').length, 0, 'rendered small after second click');
});
运行集成测试ember t -s
,浏览器访问7357端口,显示为7个测试3个失败。