来源:yoember.com
作者:Zoltan
声明:本文的转载与翻译是经过作者认可的,再次感谢原作,如有侵权请给我留言,我会删除博文!! 希望本系列教程能帮助更多学习Ember.js的初学者。
接着前面四篇:
- 环境搭建以及使用Ember.js创建第一个静态页面
- 引入计算属性、action、动态内容
- 模型,保存数据到数据库
- 发布项目,加入CRUD功能
清理模板,使用组件重构
2.0版本之后组件会越来越重要。有关组件的介绍请看Ember.js 入门指南之二十八组件定义。组件的创建同样可以使用Ember CLI命令创建。如下命令创建了2个组件,创建的同时会自动创建2个文件;一个是组件类(app/components/xxx.js
)。一个是组件对应的模板(app/templates/components/xxx.hbs
)。
1 2
| ember g component library-item ember g component library-item-form
|
修改模板library-item
下面在组件模板library-item.hbs
中增加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <div class="panel panel-default library-item"> <div class="panel-heading"> <h3 class="panel-title">{{item.name}}</h3> </div> <div class="panel-body"> <p>Address: {{item.address}}</p> <p>Phone: {{item.phone}}</p> </div> <div class="panel-footer text-right"> {{yield}} </div> </div>
|
如果注意看可以发现上述代码与app/templates/libraries/index.hbs
文件的代码非常相似。这是item
替代了model
。至于item
是怎么来的请看Ember.js 入门指南之二十九属性传递,这篇博文介绍了组件的属性传递,item
是从调用组件的模板传递过来的。上述代码中还有一个重要的东西是{{yield}}
,这个表达式与{{outlet}}
类似。同样也是一个占位符。组件渲染之后会被传进来的html代码替换。比如下面的调用代码:
1 2 3
| {{#library-item item=model}} Closed {{/library-item}}
|
组件渲染之后,上述的Closed
会替换到{{yield}}
这里,最终得到的html代码如下:
1 2 3
| <div class="panel-footer text-right"> Closed </div>
|
有关组件渲染的内容请看Ember.js 入门指南之三十包裹内容。
修改模板library-item-form
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!-- app/templates/components/library-item-form.hbs --> <div class="form-horizontal"> <div class="form-group has-feedback {{if item.isValid 'has-success'}}"> <label class="col-sm-2 control-label">Name*</label> <div class="col-sm-10"> {{input type="text" value=item.name class="form-control" placeholder="The name of the Library"}} {{#if item.isValid}}<span class="glyphicon glyphicon-ok form-control-feedback"></span>{{/if}} </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Address</label> <div class="col-sm-10"> {{input type="text" value=item.address class="form-control" placeholder="The address of the Library"}} </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Phone</label> <div class="col-sm-10"> {{input type="text" value=item.phone class="form-control" placeholder="The phone number of the Library"}} </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default" {{action 'buttonClicked' item}} disabled="{{unless item.isValid 'disabled'}}">{{buttonLabel}}</button> </div> </div> </div>
|
注意观察上述代码与libraries/new.hbs
和libraries/edit.hbs
几乎是一样的。有点不一样的是把校验移到model
中。比如校验name
属性不为空。
注意:顶部导入的代码。
1 2 3 4 5 6 7 8 9 10 11
| import Model from 'ember-data/model'; import attr from 'ember-data/attr'; import Ember from 'ember';
export default Model.extend({ name: attr('string'), address: attr('string'), phone: attr('string'),
isValid: Ember.computed.notEmpty('name') });
|
再修改app/templates/libraries/index.hbs
引入组件。
1 2 3 4 5 6 7 8 9 10 11
| <h2>List</h2> <div class="row"> {{#each model as |library|}} <div class="col-md-4"> {{#library-item item=library}} {{#link-to 'libraries.edit' library.id class='btn btn-success btn-xs'}}Edit{{/link-to}} <button class="btn btn-danger btn-xs" {{action 'deleteLibrary' library}}>Delete</button> {{/library-item}} </div> {{/each}} </div>
|
在迭代中使用组件,通过属性名item
传递迭代出来的对象library
到组件中。其中link-to
和button
这两句代码会替换到组件library-item
的{{yield}}
上。
等待项目重启完成,可以看到界面与之前的没有任何变化。页面是没有变化,但是后台的处理还需要完善。
修改app/templates/libraries/new.hbs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <h2>Add a new local Library</h2>
<div class="row">
<div class="col-md-6"> {{library-item-form item=model buttonLabel='Add to library list' action='saveLibrary'}} </div>
<div class="col-md-4"> {{#library-item item=model}} <br/> {{/library-item}} </div>
</div>
|
修改app/templates/libraries/edit.hbs
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <h2>Edit Library</h2>
<div class="row"> <div class="col-md-6"> {{library-item-form item=model buttonLabel='Save changes' action='saveLibrary'}} </div>
<div class="col-md-4"> {{#library-item item=model}} <br/> {{/library-item}} </div>
</div>
|
在组件类library-item-form.js
增加对action
的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import Ember from 'ember';
export default Ember.Component.extend({ buttonLabel: 'Save',
actions: {
buttonClicked(param) { this.sendAction('action', param); }
} });
|
原来的文件edit.hbs
和new.hbs
几乎是一样的,可以使用组件重构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <h2>{{title}}</h2>
<div class="row"> <div class="col-md-6"> {{library-item-form item=model buttonLabel=buttonLabel action='saveLibrary'}} </div>
<div class="col-md-4"> {{#library-item item=model}} <br/> {{/library-item}} </div>
</div>
|
为了实现代码复用,首先把不同的部分定义成属性:title
、buttonLabel
。默认情况下路由会渲染到同名的模板上,如果你想修改这个默认行为可以使用renderTemplate()
方法。
使用方法renderTemplate()
和setupController()
API介绍
默认情况下路由会渲染到同名的模板上,我们使用方法renderTemplate()
执行渲染的模板。比如下面的代码使用这个方法执行路由new
渲染到模板libraries/form.hbs
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import Ember from 'ember';
export default Ember.Route.extend({
model: function () { return this.store.createRecord('library'); },
setupController: function (controller, model) { this._super(controller, model);
controller.set('title', 'Create a new library'); controller.set('buttonLabel', 'Create'); },
renderTemplate() { this.render('libraries/form'); },
actions: {
saveLibrary(newLibrary) { newLibrary.save().then(() => this.transitionTo('libraries')); },
willTransition() { let model = this.controller.get('model');
if (model.get('isNew')) { model.destroyRecord(); } } } });
|
注意方法setupController()
设置组件模板中的属性title
和buttonLabel
的值。同样的在修改路由edit.js
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import Ember from 'ember';
export default Ember.Route.extend({
model(params) { return this.store.findRecord('library', params.library_id); },
setupController(controller, model) { this._super(controller, model);
controller.set('title', 'Edit library'); controller.set('buttonLabel', 'Save changes'); },
renderTemplate() { this.render('libraries/form'); },
actions: {
saveLibrary(newLibrary) { newLibrary.save().then(() => this.transitionTo('libraries')); },
willTransition(transition) { let model = this.controller.get('model');
if (model.get('hasDirtyAttributes')) { let confirmation = confirm("Your changes haven't saved yet. Would you like to leave this form?");
if (confirmation) { model.rollbackAttributes(); } else { transition.abort(); } } } } });
|
使用组件重构之后可以删除app/templates/libraries/new.hbs
和app/templates/libraries/edit.hbs
,这两个文件不需要了。效果截图如下:
使用组件nav-link-to
重构<li><a></a></li>
知道组件如何使用之后我们继续重构项目代码,重构导航模板navbar.hbs
的链接代码。使用Ember CLI命令创建组件。
1
| ember g component nav-link-to
|
这次使用扩展的方式扩展一个组件类,扩展Ember内置的组件类LinkComponent
,使用方法extend()
扩展一个类。然后使用属性tagName
指定渲染之后的标签。更多有关组件属性的介绍请看Ember.js 入门指南之三十一自定义包裹组件的HTML标签,当然你也可以参考网址的教程实现本文的需求。
1 2 3 4 5 6
| import Ember from 'ember';
export default Ember.LinkComponent.extend({ tagName: 'li' });
|
注意:记得修改Ember.Component.extend
为Ember.LinkComponent.extend
。组件模板很简单。
1 2
| <a href="">{{yield}}</a>
|
最后在修改导航模板navbar.hbs
为如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#main-navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> {{#link-to 'index' class="navbar-brand"}}Library App{{/link-to}} </div>
<div class="collapse navbar-collapse" id="main-navbar"> <ul class="nav navbar-nav"> {{#nav-link-to 'index'}}Home{{/nav-link-to}} {{#nav-link-to 'libraries'}}Libraries{{/nav-link-to}} {{#nav-link-to 'about'}}About{{/nav-link-to}} {{#nav-link-to 'contact'}}Contact{{/nav-link-to}} </ul>
<ul class="nav navbar-nav navbar-right"> <li class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin<span class="caret"></span></a> <ul class="dropdown-menu"> {{#nav-link-to 'admin.invitation'}}Invitations{{/nav-link-to}} {{#nav-link-to 'admin.contact'}}Contacts{{/nav-link-to}} </ul> </li> </ul> </div> </div> </nav>
|
等待项目重启完成,可以看到界面与之前的没有任何变化,可以任意点击导航栏菜单且不会出错。效果截图如下:
家庭作业
本篇的家庭作业就是好好理解组件!参考下面的文章认真学习、理解组件。
- Ember.js 入门指南之二十八组件定义
- Ember.js 入门指南之二十九属性传递
- Ember.js 入门指南之三十包裹内容
- Ember.js 入门指南之三十一自定义包裹组件的HTML标签
- Ember.js 入门指南之三十二处理事件
- Ember.js 入门指南之三十三action触发变化
为了照顾懒人我把完整的代码放在[GitHub](https://github.com/ubuntuvim/library-app)上,如有需要请参考参考。博文经过多次修改,博文上的代码与github代码可能有出入,不过影响不大!如果你觉得博文对你有点用,请在github项目上给我点个`star`吧。您的肯定对我来说是最大的动力!!