4.4 Routing -- Specifying A Route's Model
一、概述
应用程序中,templates被models支持。但是templates是如何知道它们应该显示哪个model呢?
例如,你有一个photos模板,它是如何知道它该呈现哪个model呢?
这就是Ember.Route工作的一部分。你可以通过定义一个和template同名的route来告诉模板呈现哪个模型,并且实现model hook。
例如,为photos模板提供一些model属性,我们定义一个route:photos对象:
app/routes/photos.js
export default Ember.Route.extend({ model() { 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" }]; } });
二、Asynchronously Loading Models(异步加载模型)
1. 在上面的示例中,模型数据是从model hook被同步返回的。这意味着数据可以立即使用,你的app不需要等待它加载,在这种情况下,因为我们马上返回数组的硬编码数据。
2. 当然,这不总是现实的。通常,数据同步将不可用,但是相反必须通过网络异步加载。例如,我们可以通过一个服务器上可用JSON API来检索照片列表。
3. 在异步数据可用的情况下,你可以从model hook仅仅返回一个promise,在呈现模板前Ember会一直等待,直到这个promise被解决。
3. 如果你对promises不熟悉,它的基本思路是,它们是代表最终值的对象。例如,如果你使用jQuery的getJSON()方法,它将会为最终通过网络返回的JSON返回一个promise。Ember使用这个promise对象去了解什么时候它有足够的数据去渲染。
4. 看一个例子。这是一个路由,它加载GitHub上最近发送到Ember.js的pull request:
app/routes/pull-requests.js
export default Ember.Route.extend({ model() { return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls'); } });
- 虽然这个例子看起来是同步的,因此很容易阅读和推理,它实际上完全是异步的。
- 因为jQuery的getJSON()方法返回一个promise。Ember检测到从一个model hook中返回了一个promise,并等待,直到这个promise解决呈现pullRequests模板。
5. 因为Ember支持promises,它可以和任何使用它们作为它的公共API的一部分的存在的库一起工作。你还可以基于promises使用很多便利使代码变得更好。
6. 例如,设想如果你需要修改上面的例子,以便模板仅仅显示最近三个pull requests。在数据传递到模板之前,我们可以依赖promise链去修改从JSON请求中返回的数据。
app/routes/pull-requests.js
export default Ember.Route.extend({ model() { var url = 'https://api.github.com/repos/emberjs/ember.js/pulls'; return Ember.$.getJSON(url).then(function(data) { return data.splice(0, 3); }); } });
三、Setting Up Controllers With The Model
所以从model hook中返回的值实际上发生了什么?
1. 默认的,从model hook返回的值将被指派给model属性相关的controller。例如,如果你的route:posts从它的model hook返回一个对象,这个对象将被设置为controller:posts的model属性。
(这一点,私底下,模板是如何知道去加载哪一个model:它们需找它们相关的controller的model属性。例如,photos模板将会呈现,无论controller:photos的model被设置为什么。)
2. 默认行为可以被改变。注意,如果你重写默认行为但是不给controller设置model属性的话,你的模板不会呈现任何数据。
四、Dynamcic Models
1. 一些路由通常展现同样的model。例如,在应用程序中路由/photos通常展现相同的photos列表。如果用户离开这个route并且稍后返回,model不会改变。
2. 然而,你会有一个路由,它的model会根据用户交互改变。例如,假设一个photo viewer app。/photos路由将会用photos列表作为model渲染photos模板,这个从来不会变。但是当用户点击一个指定的照片,我们希望用photo模板展示该模型。如果用户返回并且点击一个不同的照片,我们希望再一次展示photo模板,这次是用一个不同的model。
3. 在这样的情况下,在URL中不仅包括要显示的模板而且还包括model,这很重要。
4. 在Ember中,这是通过使用动态字段定义路由来实现的。
5. 一个动态字段是URL的一部分,它使用当前model的id来填充。动态字段通常以":"开始。
我们的photo例子定义的photo路由:
Router.map(function() { this.route('photo', { path: '/photos/:photo_id' }); });
- 在例子中,photo路由有一个动态字段:photo_id。当用户进入photo路由去展现一个指定的model(通常通过{{#link-to}})时,模型的id将会被自动放入URL中。
6. 例如,如果你转换到photo路由,使用一个id为47的model,用户浏览器中的URL将会被更新为:/photos/47。
7. 如果用户通过包含动态字段的URL直接访问app会发生什么?例如,他们可能会UI重新加载页面,或者向朋友发送链接。在这一点上, 因为我们是从暂存器启动app的,实际上JS model对象已经丢失了,我们有的仅仅是从URL上得到的ID。
8. 幸运的是,Ember将会从URL上提取任何动态字段并且将它们作为一个hash传递到model hook,作为它的第一个参数。
app/router.js
Router.map(function() { this.route('photo', { path: '/photos/:photo_id' }); });
app/routes/photo.js
export default Ember.Route.extend({ model(params) { return Ember.$.getJSON('/photos/'+params.photo_id); } });
在model hook中对于有动态字段的路由,你的工作是把ID传入一个model,它可以被路由的模板渲染。在上面的例子中,我们用照片的ID构造一个URL用于该照片的JSON表示。一旦我们拥有了URL,我们使用jQuery来为JSON模型数据返回一个promise。
9. 注意,当直接通过URL进入路由时,一个有动态字段的路由将会仅仅调用它的modle hook。如果route是通过跳转(例如link-to)进入的,模型上下文已经存在了并且钩子方法不会执行。没有动态字段的路由仍然执行model hook。
五、Refreshing Your Model
如果你的模型数据更新频繁,你可能想要定期刷新:
controller可以传递一个action给route。在上面的例子中,IndexController暴露一个getLatest action,它传递一个action调用invalidateModel。调用路由的refresh方法将会前置Ember再一次执行model hook。
六、Ember Data
许多Ember开发者使用一个model库来更容易的查找和存储记录,这比手动调用Ajax更简单。特别的,使用一个model库允许你缓存已经加载的记录,显著提高应用程序的性能。
一个流行的基于Ember的model库是Ember Data。