- Ember.js 指南
- 入门指南 - 介绍
- 入门指南 - 应用规划
- 入门指南 - 创建静态页面
- 入门指南 - 获取Ember.js和相应依赖
- 入门指南 - 添加第一个路由与模板
- 入门指南 - 建立模型
- 入门指南 - 使用静态数据
- 入门指南 - 显示模型数据
- 入门指南 - 显示模型的完成状态
- 入门指南 - 创建新的模型实例
- 入门指南 - 标记模型为完成或未完成
- 入门指南 - 显示未完成待办事项的数量
- 入门指南 - 切换显示和编辑状态
- 入门指南 - 接受修改
- 入门指南 - 删除模型
- 入门指南 - 添加子路由
- 入门指南 - 显示未完成待办事项
- 入门指南 - 显示已完成待办事项
- 入门指南 - 显示所有待办事项
- 入门指南 - 添加移除所有已完成待办事项的按钮
- 入门指南 - 提示所有待办事项已完成
- 入门指南 - 切换已完成和未完成待办事项
- 入门指南 - 更换FixtureAdapter
- 获取 Ember - 获取Ember
- 概念 - 核心概念
- 概念 - 命名惯例
- 对象模型 - 类与实例
- 对象模型 - 计算属性
- 对象模型 - 计算属性和带@each的集合数据
- 对象模型 - 观察器
- 对象模型 - 绑定
- 对象模型 - 重新打开类和实例
- 对象模型 - 绑定,观察器,计算属性:如何选择?
- 应用 - 介绍
- 模板 - 应用模板
- 模板 - Handlebars基础
- 模板 - 条件表达式
- 模板 - 显示项目列表
- 模板 - 切换作用域
- 模板 - 绑定元素属性
- 模板 - 绑定元素类名称
- 模板 - 链接
- 模板 - 操作
- 模板 - 输入助手
- 模板 - 开发助手
- 模板 - 用助手来渲染
- 模板 - 编写助手方法
- 路由 - 介绍
- 路由 - 定义路由
- 路由 - 生成的对象
- 路由 - 指定路由的模型
- 路由 - 设置控制器
- 路由 - 渲染模板
- 路由 - 重定向
- 路由 - 指定地址API
- 路由 - 查询参数
- 路由 - 异步路由
- 路由 - 加载中/错误子状态
- 路由 - 阻止和重试过渡
- 组件 - 介绍
- 组件 - 定义组件
- 组件 - 传递属性
- 组件 - 包裹内容
- 组件 - 自定义组件元素
- 组件 - 使用Action处理用户交互
- 组件 - 从组件发送操作给应用
- 控制器 - 介绍
- 控制器 - 代表单一模型
- 控制器 - 代表多模型
- 控制器 - 管理控制器间的依赖
- 模型 - 介绍
- 模型 - 定义模型
- 模型 - 创建和删除记录
- 模型 - 将记录推入仓库
- 模型 - 持久化记录
- 模型 - 查询记录
- 模型 - 使用记录
- 模型 - 使用Fixture
- 模型 - 连接HTTP服务器
- 模型 - 处理元数据
- 模型 - 自定义适配器
- 模型 - 常见问题
- 视图 - 介绍
- 视图 - 定义视图
- 视图 - 处理事件
- 视图 - 在模板中插入视图
- 视图 - 为视图添加布局
- 视图 - 自定义视图元素
- 视图 - 内置视图
- 视图 - 手动管理视图层级
- 枚举 - 介绍
- 测试 - 介绍
- 测试 - 集成测试
- 测试 - 测试助手
- 测试 - 测试用户交互
- 测试 - 单元测试
- 测试 - 单元测试基础
- 测试 - 测试组件
- 测试 - 测试控制器
- 测试 - 测试路由
- 测试 - 测试模型
- 测试 - 自动化测试
- 配置Ember.js - 禁用基本类型扩展
- 配置Ember.js - 嵌入式应用
- 配置Ember.js - 特性标识
- Cookbook - 简介
- Cookbook - 用户界面与交互
- Cookbook - 事件处理和数据绑定
- Cookbook - 助手与组件
- Cookbook - 使用对象
- 理解Ember.js - 视图层
- 理解Ember.js - 管理异步
- 理解Ember.js - 模板自动更新
- 理解Ember.js - 调试
- 理解Ember.js - 运行循环
路由 - 加载中/错误子状态
英文原文: http://emberjs.com/guides/routing/loading-and-error-substates/
除了异步路由指南中描述的技术外,Ember路由还提供了通过使用error
和loading
子状态,来实现自定义路由间的异步过渡。
loading
子状态
考虑下面的情形:
1 2 3 4 5 | App.Router.map(function() { this.resource('articles', function() { // -> ArticlesRoute this.route('overview'); // -> ArticlesOverviewRoute }); }); |
如果导航到articles/overview
,并且在ArticlesRoute#model
中,返回了一个AJAX查询承诺,来加载需要花费较长时间才能完成加载的文章集合。在这其间,UI并不会有任何关于在做什么的实际性反馈;如果通过页面刷新进入到该页面,UI将会一直是空的,因为这时还没有完成进入路由,也没有显示任何模板;如果是从其他的路由进入articles/overview
,那么会一直停留在之前路由渲染的模板,直到所有文章加载完成,这个时候articles/overview
的所有模板才会被渲染,才可见。
那么,在这个过渡的过程中该如何添加一些反馈信息呢?
loading
事件
在深入讨论加载中子状态之前,理解loading
事件的行为非常重要。
Ember路由允许通过beforeModel
/model
/afterModel
各种钩子在一个过渡的过程中返回承诺(详细介绍)。这些承诺在其被履行前会将过渡暂停。如果在其中一个钩子中返回了承诺,并且这个承诺没有理解解决,那么loading
事件就会在该路由被触发,并且一直向上冒泡到ApplicationRoute
。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | App.Router.map(function() { this.resource('foo', function() { // -> FooRoute this.route('slowModel'); // -> FooSlowModelRoute }); }); App.FooSlowModelRoute = Ember.Route.extend({ model: function() { return somePromiseThatTakesAWhileToResolve(); }, actions: { loading: function(transition, originRoute) { // displayLoadingSpinner(); // Return true to bubble this event to `FooRoute` // or `ApplicationRoute`. return true; } } }); |
如果FooSlowModelRoute
返回了一个较慢的承诺,那么loading
事件就会在FooRoute
上被触发(而不是FooSlowModelRoute
上)。
loading
事件的缺省实现
之前已经介绍了可以使用一种层次化的结构来配置loading
行为。此外,Ember还提供了一种实现了如下所示的缺省的loading
处理器。
1 2 3 4 5 6 7 | App.Router.map(function() { this.resource('foo', function() { // -> FooRoute this.resource('for.bar', function() { // -> FooBarRoute this.route('baz'); // -> FooBarBazRoute }); }); }); |
如果foo.bar.baz
路由返回了一个不会立即履行的承诺,Ember会在foo.bar.baz
的层次结构中查找可以过渡的loading
路由,查找路径为:
foo.bar.loading
foo.loading
loading
Ember会在上述的loading
路由地址找一个路由,a) 该路由可能是如下所示定义的一个路由的子类:
App.FooBarLoadingRoute
App.FooLoadingRoute
App.LoadingRoute
b) 又或者是一个按照一定规则进行命名的loading
模板:
foo/bar/loading
foo/loading
loading
在一个较慢的异步过渡过程中,如果存在loading
子状态/路由,Ember会先过渡到第一个找到的。这个到loading
子状态的中间状态的过渡会立即发生(同步的),URL也不会发生改变。与其他在当另一个异步过渡活动时发生的过渡不同,该情况下的活动异步过渡不会被取消。
当进入一个loading
子状态过渡后,对应该子状态的模板如果存在的话,会被渲染到父路由的主插口(outlet),例如foo.bar.loading
模板会被渲染到foo.bar
的插口中。(loading
路由并不是特例,所有路由都是按照这种方式工作的。)
当foo.bar.baz
主异步过渡完成时,会退出loading
子状态,渲染的模板也会被移除,并进入foo.bar.baz
,渲染其模板。
渴望型 VS 延迟型异步过渡
loading
子状态都是可选的,如果提供了loading
子状态,那么就表示强调了希望异步过渡为“渴望型”的。在缺少目标路由的loading
子状态时,路由将依然停留在之前的过渡路由,知道所有目标路由的承诺得到履行,知会在过渡完全完成是一次性过渡到目标路由(渲染模板等)。但是如果提供了一个目标路由的loading
子状态,那么就选择了“渴望型”过渡,这就表明与默认的“延迟型”不同,会首先退出当前路由(清除其模板等),并过渡到loading
子状态。除非过渡被取消或者在同一运行循环中被重定向,否则URL都会理解更新。
这里也暗含了一个错误处理,例如,当过渡到一个路由失败时,延迟过渡(默认)会依然停留在原路由,而一个渴望过渡早就离开了之前的状态路由进入到loading
子状态中了。
error
子状态
Ember为过渡提供了一种与loading
事件/子状态类似的错误处理方法。
1 2 3 4 5 | App.Router.map(function() { this.resource('articles', function() { // -> ArticlesRoute this.route('overview'); // -> ArticlesOverviewRoute }); }); |
如果ArticlesOverviewRoute
返回一个被拒绝的承诺(可能因为服务器端返回一个错误,又或者时用户没有登录等等),一个error
事件将在ArticlesOverviewRoute
被触发,并向上冒泡。可以使用这个error
事件来处理并显示一个错误消息,例如重定向到登录页面等。与loading
事件处理器实现类似,缺省的error
事件处理器也会进入一个子状态来完成处理。
例如,在ArticlesOverviewRoute#model
(或beforeModel
、afterModel
)中抛出了一个异常或者返回了一个被拒绝的承诺,那么会按照以下方式进行查找错误处理:
ArticlesErrorRoute
路由或者articles/error
模板ErrorRoute
路由或者error
模板
如果之上任意一个被找到,路由将立即过渡到该子状态(不更新URL),错误的“原因”(例如抛出的异常或拒绝的承诺)会作为model
传递给error
子状态。
如果没有找到可以访问的error
子状态,那么一条错误消息会在控制台中输出。
loading
/error
子状态处理的唯一区别是,error
从过渡的中心路由开始向上冒泡。
带动态段的error
子状态
带动态段的路由通常映射到一个模型的两个不同的层面。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | App.Router.map(function() { this.resource('foo', {path: '/foo/:id'}, function() { this.route('baz'); }); }); App.FooRoute = Ember.Route.extend({ model: function(params) { return new Ember.RSVP.Promise(function(resolve, reject) { reject("Error"); }); } }); |
在URL层次中,访问/foo/12
将会导致将foo
模板渲染到application
模板的outlet
处。当尝试加载一个foo
路由发生一个错误事件,会将顶层的error
模板渲染到application
模板的outlet
。这感觉就好像foo
路由就从未正确进入过一般。为了创建一个foo
范围的错误信息,并渲染foo/error
到foo
的outlet
中,那么需要将动态段分离:
1 2 3 4 5 6 7 | App.Router.map(function() { this.resource('foo', {path: '/foo'}, function() { this.resource('elem', {path: ':id'}, function() { this.route('baz'); }); }); }); |
遗留的LoadingRoute
之前的Ember版本(有些不慎)支持通过定义一个全局的LoadingRoute
,该路由将在过渡遇到一个较慢的承诺或者完全退出一个过渡时被激活。因为loading
模板作为顶层的视图来渲染,并没有放入到一个插口中,那么在这里处理可以显示一个加载中的指示器外几乎不能做其他的事情。与此相比较,loading
事件/子状态提供了更强的控制力,如果希望模拟与遗留的LoadingRoute
类似的行为,可以按照如下的例子来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 | App.LoadingView = Ember.View.extend({ templateName: 'global-loading', elementId: 'global-loading' }); App.ApplicationRoute = Ember.Route.extend({ actions: { loading: function() { var view = this.container.lookup('view:loading').append(); this.router.one('didTransition', view, 'destroy'); } } }); |
上例实现了一个与LoadingRoute
类型的行为,当路由进入一个loading
状态时,在顶层添加了一个视图,并在完成过渡时删除加入的视图。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论