Ember Route,路由管理,在ember中具有很重要的意义,他负责管理整个路由的规则,什么时候应该渲染什么模板等,总体来说,他的功能有
渲染一个模板加载model以供模板使用重定向到一个新的路由,比如说权限控制情况下,一个人不允许访问某一个页面可以负责处理一个action(动作或者事件)使用命令行创建ember g route <your-route-name>
; 执行命令后,会在app/templates
目录下生成模板,会在app/routes
目录下生成js文件,同时修改app/route.js
文件。
这里主要说明一下route.js
文件,其基本格式为:
当访问/path1
的时候,渲染path1
模板,当访问/path2
的时候,渲染favorites
模板。 如果是嵌套的路由(子路由),比如path1/path2
,那么形式可能是这样的, 创建的时候只需要ember g route path1/path2
如果想要设置初始页面,可以这样,这样就可以为/
设置一个初始的路由:
如果url中具有动态的参数呢?例如localhost:8000/path1/path2/2
,这个2可能是一个id,又或者localhost:8000/path1/path2/2/edit
这可能代表一个id为2的数据的编辑页面,那么在route.js
可以如下表示:
通过这种方式,就可以进行访问,但是注意,以上的形式下localhost:8000/path1/path2
是无法访问的。 在这种情况下,如何使用这个id参数呢?
如上就可以对参数进行使用。理论上来讲,是不能够修改url中的参数的,所以手动赋值是无效的,也无法进行刷新等动作。同时需要注意,不要给参数起一样的名字,那样就会不起作用,下面的李自力有2个id,所以不会起作用:
Router.map(function() { this.route('photo', { path: '/photo/:id' }, function() { this.route('comment', { path: '/comment/:id' }); });});所有的路由都有一个根的父路由:application
,也就是说,我们创建的所有路由,都是经由application
渲染的。 我们我们查看模板文件,会发现路由的模板的内容都是{{outlet}}
。每一个模板都会渲染到父模板的{{outlet}}
上。一层一层的往上渲染。 比如经典的圣杯模型,在我们看来,可能上边栏侧边栏和下边栏是不变的,那么我们可以定义我们的application.hbs
为:
那么渲染的时候,只会渲染到content中。 接下来我们讲解一下渲染的规则。这里这篇文章介绍的非常好Ember.js 入门指南之十四番外篇,路由、模板的执行、渲染顺序
一般情况下,我们要渲染的模板都是自己的哪一个,但是,ember也允许你渲染其他的模板,例如正常情况下,不做任何操作,path1/path2
渲染的模板就是app/templates/path1/path2.hbs
, 但是如果你想要进行更换,只需要重载方法renderTemplate()
:
上面这个方法可以将path1/path2
渲染到templateName
中使用{{outlet 'anOutletName'}}
的地方。详情请见render。
model的执行顺序为从主到子。例如urlpath1/path2
,那么model的执行顺序为application->path1->path2
。
首先,注意一点,就是,如果model执行完成后,才开始进行模板的渲染。 模板渲染的顺序为从子到主,也就是path2->path1->application->path1
。渲染完成后,展示。这里需要全部渲染完后展示。
路由重定向,例如,当权限控制等的时候,跳转到一个异常页面等,常常会用到,常用方法为transitionTo()
。transitionTo()
的表现和helperlink-to
的表现一致。另外还有一个方法replaceWith()
,使用起来差不多。
transitionTo()
可以在各个阶段使用:
如果是要重定向到子路由,也可以直接在beforeModel()
afterModel()
model()
中进行,但是如果这样的,考虑到子路由的执行过程,会再次将父路由执行一次,所有,ember有一个更加优化的方式redirect
还有一些高级的用法,比如复杂url或者带参数的情况,注意这种情况下不能使用replaceWith
进行替换:
有一些情况下,我们可能需要手动终止路由的跳转,或者是终止之后再重试。
当用户通过{{link-to}}、transition方法或者直接执行URL来转换路由,当前路由会自动执行willTransition方法。每个活动的路由都可以决定是否执行转换路由。 如果你正在填写一个表单,然后,无意点击了跳转,这样你的填写的数据就都丢失了,很不友好。所以一般会有一个确认页面。所以我们可以通过willTransition
来先让用户确认是否离开再说。
我们假设有这样的一个model:slow-model
,这个model返回结果非常的慢,那么我们的以routedemo1/case1
为例,如果route中加载情况如下:
那么,在slow-model
返回结果之前,页面会一直是空白,这个非常不美观,我们一般会采用一个loading的图标或者有一个提示等。
Ember提供的解决办法是:在beforeModel、model、afterModel回调还没返回前先进入一个叫loading的子状态,然后渲染一个叫<your-route-name>-loading
的模板(如果是application路由则对应的直接是loading、error不需要前缀)。
同样以demo1/path1
为例:
以上的代码,假设slow-model
是非常慢的,那么页面的展示顺序为:
以demo1/case1
为例,结合我们说过的路由渲染顺序为,注意,渲染顺序还是从子到父的: - demo1.case1-loading
- demo1-loading
- loading
(application的loading)
从上图可以看出,在model没有加载好的时候,会先用<your-route-name>-loading
来对本来的模板进行替换。那么在这个过程中,model的执行顺序呢?这个过程中,model为先执行父route的model,也就是demo1
的model,当请求发出,直到下一个子路由的mode请求发出,一直都是loading状态,也就是: 然后子路由请求发出,也就是demo1/case1
请求发出后,进入子路由的loading状态: 数据返回后,结束:
另外,在beforeModel、model、afterModel回调没有立即返回之前,会先执行一个名为loading的事件。
actions: { loading: function(transition, originRoute) { alert("Sorry this is taking so long to load!!"); } }loading事件实在model回调返回之前执行。这里需要注意一下,如果同时定义了loading
回调,以及<your-route-name>-loading
子模板,那么会只执行loading回调,而不会渲染模板。
error的情况也类似,不过过程是在model返回且失败的情况下,而loading为未返回的情况下: 以demo1/case1
为例,结合我们说过的路由渲染顺序为,注意,渲染顺序还是从子到父的(考虑到model的执行顺序,不会出现父吧子覆盖的情况,因为执行不到- -): - demo1.case1-error
- demo1-error
- error
(application的error)
error的回调为:
actions: { error(error, transition) { if (error.status === '403') { this.replaceWith('login'); } else { // Let the route above this handle the error. return true; } } }动态路由的情况,我们已经说过,但是,如果是带参数呢?举例,我们常用的分页操作,一般都会带上pageSize=10&pageNum=2
意味着第二页取10个,这样,那么在请求中怎么做呢? 这里就要使用到controller
的概念,对于路由demo1/case1
而言,如果要增加参数,那么就需要创建相应的controller
。创建controller:ember g controller demo1/case1
,以分页为例:
这样就会在请求上带上这个参数,如果你没带的话,还是会取到这个参数,只不过都是null
罢了。你可以为他们设置默认值:
那么默认就是第一页的10个。注意,如果你传的值就是和默认的一样,在请求上是不会展示的!
关于查询参数的用法,跳转,在之前的transitionTo()
方法以及link-to
助手的的时候已经说过。
在controller
中还可以调用model的数据,以及使用计算属性,官网上给出一个例子:
这样,filteredArticles
的值就会一直随着model
的改变而改变,而filteredArticles
的值是可以直接在模板中使用的,所以说间接的导致模板元素的变化。
关于查询参数,还有一个点很重要,就是更新数据。因为毕竟url改变了,如果数据却没有更新的话,简直不合理。
上述的做法相当于这样:
this.transitionTo({ queryParams: { pageNum: 3 }});{{link-to (query-param pageNum=3)}}注意,这种方式,由于没有修改路由的结构,只不过是修改了参数,所以只能说是不完整的路由切换,这种不完整,也就意味着比如model和setupController回调方法就不会被执行,只是使得controller里的属性值为新的查询参数值以及更新URL。(当然,如果是跳转到一个新的路由,还是会改变的)。
如果单纯的按照上面的写法,model是不会更新的。那么如何更新model中的数据呢?这需要你在对应的路由中配置一个名为queryParams哈希对象。并且需要设置一个名为refreshModel的查询参数,这个参数的值为true。
import Ember from 'ember';export default Ember.Route.extend({ queryParams: { category: { refreshModel: true } }, model(params) { // This gets called upon entering 'articles' route // for the first time, and we opt into refiring it upon // query param changes by setting `refreshModel:true` above. // params has format of { category: "someValueOrJustNull" }, // which we can forward to the server. return this.get('store').query('article', params); }});那么每次你修改参数后,都会更新model并且执行setupController。但是要注意,这个也会同时刷新父路由!
那么如果要手动触发呢?比如搜索条件,你想要点击搜索的时候才更新数据,或者说只更新自己的路由的信息,怎么办呢?那么可以这么做
import Ember from 'ember';export default Ember.Route.extend({ model(){ console.log('model'); }, setupCotroller(controller,model){ console.log('setupcontroller'); }, actions:{ //如果只需要更新model clickAction(){ this.refresh(); }, //同时更新model以及setupcontroller clickAction1(){ let self = this, controller=this.get('controller'); this.model().then((data)=>{ controller.set('model', data); self.setupController(controller,data); }) } }});//TODO
新闻热点
疑难解答