本文概述
- 我们要建造什么?
- 路线:Ember.js应用程序的钥匙
- 显示艺术家列表
- 嵌套路线以获取嵌套视图
- 创作艺术家
- 绕道:真理的单一来源
- 显示歌手的歌曲
- 星级评分小部件
- 包装全部
- 进一步阅读(和观看)
我们要建造什么? 这是我们的” 摇滚” 应用最终版的外观:
文章图片
我使用了出色的Ember Inspector插件(Chrome和Firefox均存在), 以易于阅读的方式向你显示生成的路由。以下是Ember路由的基本规则, 你可以借助上表针对我们的特殊情况进行验证:
- 有一个隐式的应用程序路由。
这将为所有请求(转换)激活。
- 有一个隐式索引路由。
【建立第一个Ember.js应用程序的指南】当用户导航到应用程序的根目录时, 将输入此内容。
- 每个资源路由都会创建一个具有相同名称的路由, 并在其下方隐式创建一个索引路由。
当用户导航到该路线时, 此索引路线将被激活。在我们的例子中, 当用户导航到/ artists时, artist.index被触发。
- 简单(非资源)嵌套路由将以其父路由名称作为前缀。
我们定义为this.route(‘ songs’ , … )的路线将以artist.songs为名称。当用户导航到/ artists / pearl-jam或/ artists / radiohead时, 它会被触发。
- 如果未给出路径, 则假定该路径等于路由名称。
- 如果路径包含:, 则将其视为动态段。
分配给它的名称(在我们的示例中为slug)将与url相应段中的值匹配。上面的段段的值将为Pearl-jam, radiohead或从URL中提取的任何其他值。
文章图片
绕道:真理的单一来源 考虑一下。借助绑定和计算的属性, 我们可以将数据建立(模型)为真实的唯一来源。上方, 新艺术家名称的更改触发了controller属性的更改, 而后者又触发了Disabled属性的更改。当用户开始键入新艺术家的名称时, 该按钮将变为启用状态, 就好像是在魔术一样。
系统越大, 我们从” 单一真相” 原则中获得的杠杆作用就越大。它使我们的代码干净, 健壮, 并使我们的属性定义更具声明性。
其他一些框架也强调将模型数据作为事实的唯一来源, 但是要么没有达到Ember的水平, 要么没有做到如此彻底的工作。例如, Angular具有双向绑定, 但没有计算属性。它可以通过简单的函数” 模拟” 计算出的属性。这里的问题是, 它无法知道何时刷新” 计算属性” , 因此只能进行脏检查, 从而导致性能下降, 尤其是在较大的应用程序中尤其如此。
如果你想了解更多有关该主题的信息, 建议你阅读eviltrout的博客文章以获取更短的版本, 或者阅读Quora问题以进行更长的讨论, 双方的核心开发人员都应参与其中。
第2部分:动作处理程序
让我们回过头来看一下createArtist动作在触发后如何创建(按下按钮之后):
App.ArtistsRoute = Ember.Route.extend({
...
actions: {
createArtist: function() {
var name = this.get('controller').get('newName');
Ember.$.ajax('http://localhost:9393/artists', {
type: 'POST', dataType: 'json', data: { name: name }, context: this, success: function(data) {
var artist = App.Artist.createRecord(data);
this.modelFor('artists').pushObject(artist);
this.get('controller').set('newName', '');
this.transitionTo('artists.songs', artist);
}, error: function() {
alert('Failed to save artist');
}
});
}
}
});
动作处理程序需要包装在动作对象中, 并且可以在路线, 控制器或视图上定义。我选择在此处的路由上定义它, 因为操作的结果并不局限于控制器, 而是” 全局” 的。
这里没有任何幻想。在后端通知我们保存操作成功完成之后, 我们按顺序执行三件事:
- 将新艺术家添加到模板模型(所有艺术家)中, 以便重新渲染它, 新艺术家显示在列表的最后一项。
- 通过newName绑定清除输入字段, 从而使我们不必直接操作DOM。
- 过渡到新路线(艺术家, 歌曲), 并传入新创建的艺术家作为该路线的模型。 transitionTo是内部在路线之间移动的方式。 (链接到帮助程序可通过用户操作来实现。)
这里的活动路线是artist.songs, 因此控制器和模板将分别为ArtistsSongsController和artist / songs。我们已经看到了如何将模板渲染到artist模板提供的输出中, 因此我们可以只关注手头的模板:
<
script type="text/x-handlebars" data-template-name="artists/songs">
(...)
{{#each songs}}
<
div class="list-group-item">
{{title}}
{{view App.StarRating maxRating=5}}
<
/div>
{{/each}}
<
/script>
请注意, 我剥离了代码以创建新歌曲, 因为它与创建新歌手的歌曲完全相同。
根据服务器返回的数据, 在所有艺术家对象中设置了songs属性。完成此操作的确切机制对当前的讨论兴趣不大。现在, 我们足以知道每首歌曲都有标题和等级。
标题直接显示在模板中, 而评分通过StarRating视图由星星表示。让我们现在来看。
星级评分小部件 歌曲的评分介于1到5之间, 并通过App.StarRating视图显示给用户。视图可以访问其上下文(在本例中为歌曲)及其控制器。这意味着他们可以读取和修改其属性。这与另一个Ember构建块相反, 这些Ember构件是隔离的, 可重用的控件, 它们只能访问传递给它们的内容。 (在此示例中, 我们也可以使用星级评分组件。)
让我们看看当用户点击其中一颗星时, 视图如何显示星数并设置歌曲的等级:
App.StarRating = Ember.View.extend({
classNames: ['rating-panel'], templateName: 'star-rating', rating: Ember.computed.alias('context.rating'), fullStars: Ember.computed.alias('rating'), numStars:Ember.computed.alias('maxRating'), stars: function() {
var ratings = [];
var fullStars = this.starRange(1, this.get('fullStars'), 'full');
var emptyStars = this.starRange(this.get('fullStars') + 1, this.get('numStars'), 'empty');
Array.prototype.push.apply(ratings, fullStars);
Array.prototype.push.apply(ratings, emptyStars);
return ratings;
}.property('fullStars', 'numStars'), starRange: function(start, end, type) {
var starsData = http://www.srcmini.com/[];
for (i = start;
i <
= end;
i++) {
starsData.push({ rating: i, full: type ==='full' });
};
return starsData;
}, (...)
});
等级, fullStars和numStars是我们先前与ArtistsController的disabled属性讨论的计算属性。上面, 我使用了所谓的计算属性宏, 在Ember中定义了大约十二个宏。它们使典型的计算属性更简洁, 更不易出错(写)。我将等级设置为上下文(进而是歌曲)的等级, 同时定义了fullStars和numStars属性, 以便它们在” 星级” 小部件的上下文中更好地阅读。
星星法是主要吸引力。它返回一个恒星数据数组, 其中每个项目都包含一个等级属性(从1到5)和一个标志(满), 以指示该星星是否满。这使得在模板中遍历它们非常简单:
<
script type="text/x-handlebars" data-template-name="star-rating">
{{#each view.stars}}
<
span {{bind-attr data-rating=rating}}
{{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}}
{{action "setRating" target=view}}>
<
/span>
{{/each}}
<
/script>
此代码段包含几点注意事项:
- 首先, 每个帮助程序通过在属性名称前面加上view来指定其使用view属性(而不是控制器上的属性)。
- 其次, span标记的class属性分配了混合的动态和静态类。任何以:开头的内容都将成为静态类, 而full:glyphicon-star:glyphicon-star-empty表示法类似于JavaScript中的三元运算符:如果full属性为true, 则应分配第一个类;如果没有, 第二。
- 最后, 单击标签后, 应该触发setRating动作-但是Ember会在视图上查找它, 而不是在路由或控制器上进行查找, 就像创建新艺术家一样。
App.StarRating = Ember.View.extend({
(...)
actions: {
setRating: function() {
var newRating = $(event.target).data('rating');
this.set('rating', newRating);
}
}
});
我们从模板中分配的评级数据属性中获得评级, 然后将其设置为歌曲的评级。请注意, 新评分不会在后端持续存在。根据我们如何创建艺术家并将其作为有动机的读者的练习来实施此功能并不困难。
包装全部 我们品尝了上述灰烬蛋糕的几种成分:
- 我们已经看到路由如何成为Ember应用程序的症结所在, 以及它们如何用作命名约定的基础。
- 我们已经看到双向数据绑定和计算属性如何使我们的模型数据成为事实的单一来源, 并允许我们避免直接的DOM操作。
- 我们已经看到了如何以多种方式触发和处理动作, 以及如何构建自定义视图来创建不属于HTML的控件。
进一步阅读(和观看) 对于Ember而言, 除了我仅能胜任本职工作之外, 还拥有更多。如果你想观看有关我如何构建上述应用程序的更高级版本的截屏视频, 和/或了解有关Ember的更多信息, 则可以注册我的邮件列表, 每周获取文章或提示。
希望我能激发你的兴趣, 以了解有关Ember.js的更多信息, 并且你已经超越了本文中使用的示例应用程序。在继续学习Ember.js时, 请务必浏览一下我们关于Ember Data的文章, 以了解如何使用ember数据库。玩得开心!
相关:Ember.js和开发人员最常犯的8个错误
推荐阅读
- Django,Flask和Redis教程(Python框架之间的Web应用程序会话管理)
- AngularJS教程(自定义指令的神秘化)
- 建立WebRTC应用程序的一年(启动工程学)
- Ember Data(Ember数据库的综合教程)
- 你的第一个AngularJS应用的分步教程
- 快速应用程序开发框架AllcountJS进行开发
- 10个最常见的Web安全漏洞
- Web API设计的5条黄金法则
- 为什么我决定拥抱Laravel