Ember 翻译——教程九:创建一个复杂的组件
来源:互联网 发布:linux maven 打包war 编辑:程序博客网 时间:2024/06/06 20:26
当用户搜索租赁信息时,他们或许也想要缩小他们的搜索范围到某一特定城市。让我们创建一个组件来让他们依照城市过滤租赁信息。
一开始,让我们生成新的组件。我们将其命名为 list-filter
,因为我们想要我们的组件对租赁信息基于用户输入进行过滤。
ember g component list-filter
和之前一样,这会创建一个 Handlebars 模板(app/templates/components/list-filter.hbs
),一个 JavaScript 文件(app/components/list-filter.js
),和一个组件继承测试(tests/integration/components/list-filter.js
)。
让我们从写一些测试开始,以便于帮助我们思考我们将做什么。过滤组件将生成一系列经过过滤的信息,不管它里面呈现的是什么,成为其内部模板块。我们想要我们的组件调用两种行为:一种是在没提供过滤信息时提供所有的信息,而另一种是依照城市搜索列表。
对于我们的初次测试,我们将检查是否我们提供的所有城市都被呈现了出来以及是否可以从模板中访问列表对象。
我们的依照城市调用过滤的行为将异步完成,而我们的测试也将必须适应这一点。我们将利用这里的 行为 来处理 filterByCity
中异步事件的完成,它是从我们的存根事件中返回一个 promise 实现的。
注意在在测试的末尾,我们也需要添加一个 wait
调用来确保测试结果。Ember 的 wait
helper 会在运行给定的回调函数和结束测试之前,先等待所有其它的 promise 切换到 resovle 状态。
/tests/integration/components/list-filter-test.js
import { moduleForComponent, test } from 'ember-qunit';import hbs from 'htmlbars-inline-precompile';import wait from 'ember-test-helpers/wait';import RSVP from 'rsvp';moduleForComponent('list-filter', 'Integration | Component | filter listing', { integration: true});const ITEMS = [{city: 'San Francisco'}, {city: 'Portland'}, {city: 'Seattle'}];const FILTERED_ITEMS = [{city: 'San Francisco'}];test('should initially load all listings', function (assert) { // we want our actions to return promises, since they are potentially fetching data asynchronously this.on('filterByCity', (val) => { if (val === '') { return RSVP.resolve(ITEMS); } else { return RSVP.resolve(FILTERED_ITEMS); } }); // with an integration test, you can set up and use your component in the same way your application // will use it. this.render(hbs` {{#list-filter filter=(action 'filterByCity') as |results|}} <ul> {{#each results as |item|}} <li class="city"> {{item.city}} </li> {{/each}} </ul> {{/list-filter}} `); // the wait function will return a promise that will wait for all promises // and xhr requests to resolve before running the contents of the then block. return wait().then(() => { assert.equal(this.$('.city').length, 3); assert.equal(this.$('.city').first().text().trim(), 'San Francisco'); });});
对于我们的第二个测试,我们将检查在过滤器中输入文字是否真的会调用适当的过滤行为和更新显示的列表。
我们通过在我们的输入框生成一个 keyUp
事件来触发该行为,然后检查是否只呈现了一条信息。
/tests/integration/components/list-filter-test.js
test('should update with matching listings', function (assert) { this.on('filterByCity', (val) => { if (val === '') { return RSVP.resolve(ITEMS); } else { return RSVP.resolve(FILTERED_ITEMS); } }); this.render(hbs` {{#list-filter filter=(action 'filterByCity') as |results|}} <ul> {{#each results as |item|}} <li class="city"> {{item.city}} </li> {{/each}} </ul> {{/list-filter}} `); // The keyup event here should invoke an action that will cause the list to be filtered this.$('.list-filter input').val('San').keyup(); return wait().then(() => { assert.equal(this.$('.city').length, 1); assert.equal(this.$('.city').text().trim(), 'San Francisco'); });});
下一步,在我们的 app/templates/rentals.hbs
文件中,我们将通过类似的方式添加测试中使用的新的 list-filter
组件。我们将使用我们的 rental-listing
组件来展示详细的租赁信息,而非只显示城市。
/app/templates/rentals.hbs
<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>{{#list-filter filter=(action 'filterByCity') as |rentals|}} <ul class="results"> {{#each rentals as |rentalUnit|}} <li>{{rental-listing rental=rentalUnit}}</li> {{/each}} </ul>{{/list-filter}}
既然我们有了失败的测试和关于我们组件的想法,我们这就来创建我们的组件。我盟想要只是简单地提供一个输入字段和一个结果将展示到模板块中的字段,所以我们的模板将会很简单:
/app/remplates/components/list-filter.hbs
{{input value=value key-up=(action 'handleFilterEntry') class="light" placeholder="Filter By City"}}{{yield results}}
这个模板包含了一个 {{input}}
helper,它将渲染为一个输入框,用户可以输入一个指令来过滤搜索中使用的城市列表。input
的 value
属性将会绑定到组件的 value
属性上。key-up
属性将会被绑定到 handleFilterEntry
行为上。
组件的 JavaScript 代码如下:
/app/components/list-filter.js
import Ember from 'ember';export default Ember.Component.extend({ classNames: ['list-filter'], value: '', init() { this._super(...arguments); this.get('filter')('').then((results) => this.set('results', results)); }, actions: { handleFilterEntry() { let filterInputValue = this.get('value'); let filterAction = this.get('filter'); filterAction(filterInputValue).then((filterResults) => this.set('results', filterResults)); } }});
我们通过用空值调用 filter
行为来使用 init
钩子,从而设置我们的初始化列表。我们的 handleFilterEntry
行为将基于我们 input helper 设置的 value
属性来调用过滤行为。
filter
行为是被调用对象传入其中的,我们将创建一个 rentals
控制器。控制器能够包含一些行为和属性,这些行为和属性可以被控制器相关的路由的模板所读取。
通过运行下面的代码为 rentals
路由生成一个控制器:
ember g controller rentals
现在,请像这样定义你的新控制器:
/app/controllers/rentals.js
import Ember from 'ember';export default Ember.Controller.extend({ actions: { filterByCity(param) { if (param !== '') { return this.get('store').query('rental', { city: param }); } else { return this.get('store').findAll('rental'); } } }});
当用户在组件的输入框内输入内容时,控制器中的 filterByCity
行为就会被调用。这个行为将取得 value
属性,然后过滤出 data store 中匹配用户输入信息的租赁信息。查询的结果将会被返回到调用程序中。
为了让这个行为工作,我们需要用下面的代码替换我们的 Mirage 的 config.js
文件,以便于它能响应我们的查询。
mirage/config.js
export default function() { this.namespace = '/api'; let rentals = [{ type: 'rentals', id: 'grand-old-mansion', attributes: { title: 'Grand Old Mansion', owner: 'Veruca Salt', city: 'San Francisco', type: 'Estate', bedrooms: 15, image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg', description: "This grand old mansion sits on over 100 acres of rolling hills and dense redwood forests." } }, { type: 'rentals', id: 'urban-living', attributes: { title: 'Urban Living', owner: 'Mike Teavee', city: 'Seattle', type: 'Condo', bedrooms: 1, image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg', description: "A commuters dream. This rental is within walking distance of 2 bus stops and the Metro." } }, { type: 'rentals', id: 'downtown-charm', attributes: { title: 'Downtown Charm', owner: 'Violet Beauregarde', city: 'Portland', type: 'Apartment', bedrooms: 3, image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg', description: "Convenience is at your doorstep with this charming downtown rental. Great restaurants and active night life are within a few feet." } }]; this.get('/rentals', function(db, request) { if(request.queryParams.city !== undefined) { let filteredRentals = rentals.filter(function(i) { return i.attributes.city.toLowerCase().indexOf(request.queryParams.city.toLowerCase()) !== -1; }); return { data: filteredRentals }; } else { return { data: rentals }; } });}
在更新完我们的 mirage 配置之后,我们应该看到通过的测试以及在首页上出现一个简单的过滤器,它将随你的输入更新租赁信息列表:
原文地址
- Ember 翻译——教程九:创建一个复杂的组件
- Ember 翻译——教程七:创建一个简单的组件
- Ember 翻译——教程八:创建一个 Handlebars helper
- Ember 翻译——教程一:创建你的 APP
- Ember旅程系列(九) -- 构建一个比较复杂的组件
- Ember 翻译——教程六:使用 Ember Data
- Ember 翻译——教程十二:部署
- Ember 翻译——教程二:设置测试
- Ember 翻译——教程三:路由和模板
- Ember 翻译——教程四:模型钩子
- Ember 翻译——教程五:安装插件
- Ember 翻译——教程十:服务和功能
- Ember 翻译——教程十一:添加嵌套路由
- Ember 翻译——入门二:安装 Ember
- Ember 翻译——对象模型:一、Ember 中的对象
- Ember 翻译——官网首页
- Ember 翻译——引导页
- Ember 翻译——路由:介绍
- Android开发——垂直水平滑动条scrollView和HorizontalScrollView的嵌套使用
- 数据立方体与OLAP
- 红黑树
- 多线程下载速率提高的原理
- 其实,我是个java程序员。
- Ember 翻译——教程九:创建一个复杂的组件
- Android开发——HorizontalScrollView实现侧滑效果
- Java基础 - 跳表(SkipList)
- github团队开发--组建自己的组织(Organization)
- C 顺序表求交集和并集
- C语言 链表求一组数据的交集并集
- 购物车实现
- Struts2体系结构与基本流程
- 计算机中的负数