接下来,我们希望能够创造艺术家,而不仅仅是看一个无聊的列表.
下面是上述过程的可视化表示. 总而言之:当用户输入艺术家的名字时 新名称
属性更新,然后是 禁用
属性,最后将艺术家的名字添加到列表中.
< / div >
绕路:真理的单一来源
想想看. 借助绑定和计算属性,我们可以将(模型)数据建立为 真理的单一来源. 以上, 新艺术家名称的更改会触发控制器属性的更改, 进而触发禁用属性的更改. 当用户开始输入新艺术家的名字时,按钮就会被启用,就像变魔术一样.
系统越大,我们从“单一真相来源”原则中获得的杠杆作用就越大. 它使我们的代码保持整洁和健壮,并且使我们的属性定义更具声明性.
其他一些框架也强调将模型数据作为事实的单一来源,但要么没有灰烬做得那么好,要么没有做得那么彻底. 例如,角有双向绑定——但没有计算属性. It can “emulate” 计算 properties through simple functions; the problem here is that it has no way of knowing when to refresh a “计算 财产” 和 thus resorts to dirty checking 和, 反过来, 导致性能损失, 尤其是在大型应用程序中.
如果您想了解更多有关该主题的信息,我建议您阅读 恶魔鳟鱼的博客 对于较短的版本或 这个Quora问题 为了让双方核心开发者参与更长的讨论.
第2部分:操作处理程序
让我们回去看看 create艺术家
动作是在它被触发后创建的(在按下按钮之后):
应用程序.艺术家路线 = 灰烬.路线.扩展({
...
行动:{
create艺术家:函数(){
Var name = 这.get(控制器).(新名称);
灰烬.$.ajax (http://localhost: 9393 /艺术家,{
类型:“文章”,
数据类型:json,
数据:{name: name},
背景:这个,
成功:function(data) {
var artist = 应用程序.艺术家.createRecord(数据);
这.梅尔('艺术家').推Object(艺术家);
这.get(控制器).集(新名称,”);
这.transitionTo(“艺术家.歌”,艺术家);
},
错误:function() {
alert('未能保存艺术家');
}
});
}
}
});
操作处理程序需要包装在 行动
对象,可以在路由、控制器或视图上定义. 我选择在这里的路由中定义它,因为动作的结果并不局限于控制器,而是, “全球”.
这里没有什么特别的. 后端通知我们保存操作成功完成后, 我们做三件事, 在顺序:
- 将新的艺术家添加到模板(所有艺术家)的模型中,以便它被重新渲染,并且新艺术家显示为列表的最后一项.
- 清除输入字段
新名称
绑定,使我们不必直接操作DOM.
- 转到新路线(
艺术家.歌曲
),将新创建的艺术家作为这条路线的模型. transitionTo
在内部路线之间移动的方式. ( 链接到
Helper通过用户操作来完成这个任务.)
展示艺术家的歌曲
我们可以通过点击艺术家的名字来显示艺术家的歌曲. 我们也让艺术家进来,他将成为新路线的模特. 如果模型对象就这样被传入,则 模型
路由的钩子将不会被调用,因为不需要解析模型.
这里的活动路径是 艺术家.歌曲
因此控制器和模板将是 艺术家SongsController
和 艺术家/歌曲
分别. 我们已经看到了模板是如何被渲染到 艺术家
模板,这样我们就可以只关注手头的模板:
请注意,我删除了创建新歌的代码,因为它与创建新艺术家的代码完全相同.
的 歌曲
属性根据服务器返回的数据在所有艺术家对象中设置. 这一过程的确切机制并没有引起科学家的兴趣
当前讨论. 现在,我们知道每首歌都有一个标题和评分就足够了.
标题直接显示在模板中,评级由星星表示 StarRating
视图. 我们来看看.
歌曲的评分在1到5之间,并通过视图显示给用户, 应用程序.StarRating
. 视图可以访问它们的上下文(在本例中是歌曲)和它们的控制器. 这意味着他们可以读取和修改它的属性. 这与另一个烬构建块形成对比, 组件, 它们是孤立的, 可重用的控件,只能访问传递给它们的内容. (我们也可以在这个例子中使用星级评价组件.)
让我们看看视图如何显示星星的数量,并在用户点击其中一个星星时设置歌曲的评级:
应用程序.StarRating =灰烬.视图.扩展({
一会:“评级-panel”,
templateName:“星级”,
评级:灰烬.计算.别名(“上下文.评级”),
完整的Stars:灰烬.计算.别名(“评级”),
numStars:灰烬.计算.别名(“maxRating”),
*: function() {
Var评级= [];
var 完整的Stars = 这.starRange(1,这.(“完整的Stars”),“满”);
var emptyStars = 这.starRange(这.get('完整的Stars') + 1,这.(“numStars”),“空”);
数组.原型.推.应用(评级,完整的Stars);
数组.原型.推.应用(评级,emptyStars);
返回评级;
}.产权(“完整的Stars”、“numStars”),
starRange:函数(start, end, type) {
var 星星Data = [];
for (i = start; i <= end; i++) {
星星Data.Push ({评级: i, 完整的: type === '完整的'});
};
返回星星Data;
},
(...)
});
评级
, 完整的Stars
和 numStars
我们之前讨论过的计算属性是 禁用
的性质 艺术家Controller
. 上面,我使用了一个所谓的计算属性宏,其中大约有十几个是在灰烬中定义的. 它们使典型的计算属性更简洁,更不容易出错(编写)。. 我设置 评级
是对上下文的评价(因此也是对歌曲的评价),而我定义了 完整的Stars
和 numStars
属性,以便它们在星级评定小部件的上下文中更好地阅读.
的 星星
方法是主要的吸引力. 它返回一个星号数据数组,其中每个项目都包含一个 评级
属性(从1到5)和标志(完整的
)来表示星星是否满了. 这使得在模板中遍历它们变得非常简单:
这段代码包含几个注意事项:
- 首先,
每一个
Helper指定它
通过前缀使用视图属性(而不是控制器上的属性)
带有的属性名 视图
.
- 第二,
class
属性
分配的混合动态类和静态类. 任何以a为前缀的 :
变成了一个
静态类,而 全部:glyphicon-star: glyphicon-star-empty
符号就像
JavaScript中的三元运算符:如果完整的属性为真,
the first class should be assigned; if 不, the second.
- 最后,当单击标记时,
setRating
action应该被触发——但是灰烬会在视图中查找它, 不是路由或控制器, 就像创造一个新的艺术家一样.
因此,在视图上定义了操作:
应用程序.StarRating =灰烬.视图.扩展({
(...)
行动:{
setRating: function() {
var newRating = $(事件.目标).数据(“评级”);
这.设置(“评级”,newRating);
}
}
});
我们从 评级
属性,然后将其设置为 评级
为了这首歌. 请注意,新评级不会在后端持久化. 基于我们创造美工的方式,实现这一功能并不是一件难事,它留给有动机的读者作为练习.
总结一下
我们已经品尝了前面提到的余烬蛋糕的几种成分:
- 我们已经看到了路由如何成为灰烬应用程序的关键,以及它们如何作为命名约定的基础.
- 我们已经看到了双向数据绑定和计算属性如何使模型数据成为事实的单一来源,并允许我们避免直接的DOM操作.
- 我们已经看到了如何以几种方式触发和处理动作以及如何构建一个自定义视图来创建一个控件 不 HTML的一部分.
很漂亮,不是吗?
进一步阅读(和观看)
关于烬的内容远远超过我在这篇文章中所能描述的. 如果你想看一个关于我如何构建上述应用程序的更先进版本和/或了解更多关于灰烬的视频系列, 你可以 加入我的邮件列表 每周获得文章或提示.
我希望我已经激起了你对烬的更多了解.js和你远远超出了我在这篇文章中使用的示例应用程序. 当你继续了解烬.请务必看一下我们的 关于灰烬 Data的文章,以学习如何使用灰烬 - Data库. 玩得开心!
相关: 灰烬.js和开发者常犯的8个错误
< / div >
< / div >< / div >< / div >
标签
< / div >< / div >< / div >< / div >
就这一主题咨询作者或专家.< / div >
预约电话< / div >< / div >