发布于 2015-08-18 16:32:35 | 424 次阅读 | 评论: 0 | 来源: 网络整理
应用中的模板背后是由模型来支撑的。那么模板是如何知道需要显示哪些模型的呢?
例如,如果有一个photos
模板,那么它是如何知道应该渲染哪一个模型的呢?
这正是Ember.Route
的工作之一。通过定义一个与模板同名的,并实现其model
方法的路由,是一种指定模板需要渲染的模型的方法。
例如,为了给photos
模板提供一些模型数据,可以定义一个App.PhotosRoute
对象:
1 2 3 4 5 6 7 8 9 10 11 |
App.PhotosRoute = Ember.Route.extend({ model: function() { return [{ title: "Tomster", url: "http://emberjs.com/images/about/ember-productivity-sm.png" }, { title: "Eiffel Tower", url: "http://emberjs.com/images/about/ember-structure-sm.png" }]; } }); |
在上述例子中,模型数据在model
钩子中是被同步返回的。也就是说数据是立即可用的,应用不需要花时间等待数据加载,例子中model
钩子里面直接返回了硬编码的一组数据。
当然这与实际的情况并不总是吻合。更常见的是数据并不是同步的,而是需要通过网络异步加载。例如,可能需要通过一个服务端的JSON API来获取照片列表。
在数据是异步加载时,在model
钩子里面只需要返回一个承诺,Ember就会等待承诺履行后才开始渲染模板。
如果对承诺不熟悉,可以姑且将其认为承诺就是代表了最终加载的数据的对象。例如,使用jQuery的getJSON
方法,该方法就返回一个代表最终从网络加载的JSON的承诺。Ember使用这个承诺对象知道何时拥有了足够用来渲染的数据。
更多关于承诺的详细介绍,请参看异步路由指南中的关于承诺部分。
下面看一个实际的例子。这里有一个从GitHub上获取最近的Ember.js的PR的路由:
1 2 3 4 5 |
App.PullRequestsRoute = Ember.Route.extend({ model: function() { return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls'); } }); |
为了使得代码更加易读,这个例子看上去跟同步的没有什么两样,但是实际上它是异步完成的。这是因为jQuery的getJSON()
方法返回了一个承诺。Ember会检测到model
钩子返回的是一个承诺,然后一直等待直至承诺被履行时才渲染pullRequests
模板。
(更多关于jQuery的XHR函数,请查看jQuery文档jQuery.ajax。)
因为Ember支持承诺,这使得Ember可以与所有采用承诺作为公共API一部分的持久化库一起工作。此外,利用关于承诺的内置惯例,还可以让代码变得更加清晰。
例如,假设需要修改上例,让模板只显示最近三个PR。通过采用承诺链,可以在将数据传递给模板之前修改请求返回的JSON:
1 2 3 4 5 6 7 8 |
App.PullRequestsRoute = Ember.Route.extend({ model: function() { var url = 'https://api.github.com/repos/emberjs/ember.js/pulls'; return Ember.$.getJSON(url).then(function(data) { return data.splice(0, 3); }); } }); |
model
钩子返回的数据上到底发生了些什么呢?
在默认情况下,model
钩子返回的值,会设置为关联的控制器的model
属性。例如,如果App.PostsRoute
通过model
钩子返回了一个对象,这个对象会设置为App.PostsController
的model
属性。
(模板是如何知道该使用哪个模型进行渲染的呢?模板通过查找其关联的控制器的model
属性来进行渲染。例如,photos
模板将会使用App.PhotosController
的model
属性来进行渲染。)
查看设置控制器指南一节,可以知道如何改变这个缺省行为。注意,如果重写了这一缺省行为,且未给控制器设置model
属性,那么模板就无法获得用来渲染的数据。
有的路由总是显示相同的模型。例如,/photos
路由将总是显示应用中所有照片的列表。用户离开该路由,然后再回来这个模型也不会发生改变。
然而,有的路由却需要根据用户的交互来显示不同的模型。例如,照片浏览器应用。/photos
路由将使用照片列表作为模型来渲染photos
模板,这个模型不会改变。但是当用户点击一个特定的照片,那么需要在photo
模板中显示被选定的照片。如果用户回头点击另外一张不同的照片,这时需要一个不同的模型来渲染photo
模板。
在这种情况下,需要在URL中不仅包含要渲染哪一个模板,还要知道使用哪一个模型。
在Ember中,通过动态段来定义路由可以完成该任务。
一个动态段是URL的一部分,动态段最终将被模型的ID取代。动态段的定义以分号(:
)开始。照片例子中photo
路由可以如下所示进行定义:
1 2 3 |
App.Router.map(function() { this.resource('photo', { path: '/photos/:photo_id' }); }); |
在上例中,photo
路由有一个动态段:photo_id
。当用户进入photo
路由来显示一个特定的照片模型时(通常是通过{{link-to}}
助手),模型的ID会被自动的添加到URL中。
查看链接可以查看更多关于使用{{link-to}}
助手,传递一个模型,然后链接到指定路由。
例如,如果使用一个id
属性值为47
的模型,过渡到photo
路由,那么在用户浏览器中的URL会更新为:
1 |
/photos/47 |
那么如果用户直接通过浏览器输入一个包含动态段的URL会发生什么呢?例如,用户可能重新加载页面,或者发送一个链接给朋友等。这时,由于应用是重新启动的,用于显示的Javascript模型对象已经不存在,所能得到的就只有模型的ID。
幸运的是,Ember会从URL中抽出动态段部分,并将其作为第一个参数,传递给model
钩子:
1 2 3 4 5 6 7 8 9 |
App.Router.map(function() { this.resource('photo', { path: '/photos/:photo_id' }); }); App.PhotoRoute = Ember.Route.extend({ model: function(params) { return Ember.$.getJSON('/photos/'+params.photo_id); } }); |
在有动态段的路由的model
钩子中,需要通过传入的ID(比如47
或者post-slug
),转换为需要用来渲染模板的模型。在上面的例子中,使用了照片的ID(params.photo_id
)来构造一个URL,来获取照片的JSON格式表示。一旦有了URL,就可以使用jQuery来获取一个表示JSON模型数据的一个承诺。
注意:一个具有动态段的路由只有在通过URL访问的时候,model
钩子才会被调用。如果路由是从一个跳转进入的(例如:使用Handlebars的link-to助手时),模型上下文已经准备好了,因此model
钩子这时不会被执行。没有动态段的路由其model
钩子每次都会被执行。
许多Ember开发者都会使用一个模型库来处理查询、保存记录,这样比手动处理AJAX调用要简单很多。特别是使用一个可以缓存已经加载记录的模型库,会大大提升应用的性能。
为Ember定制的一个流行的模型库是Ember Data。需要了解更多关于使用Ember Data来管理模型的内容,可以查看模型指南一章。