首页 > 网站 > WEB开发 > 正文

Ember Route

2024-04-27 15:13:45
字体:
来源:转载
供稿:网友

简介

Ember Route,路由管理,在ember中具有很重要的意义,他负责管理整个路由的规则,什么时候应该渲染什么模板等,总体来说,他的功能有

渲染一个模板加载model以供模板使用重定向到一个新的路由,比如说权限控制情况下,一个人不允许访问某一个页面可以负责处理一个action(动作或者事件)

基本配置

创建route

使用命令行创建ember g route <your-route-name>; 执行命令后,会在app/templates目录下生成模板,会在app/routes目录下生成js文件,同时修改app/route.js文件。

这里主要说明一下route.js文件,其基本格式为:

Router.map(function() { this.route('path1'); this.route('favorites', { path: '/path2' });});

当访问/path1的时候,渲染path1模板,当访问/path2的时候,渲染favorites模板。 如果是嵌套的路由(子路由),比如path1/path2,那么形式可能是这样的, 创建的时候只需要ember g route path1/path2

Router.map(function() { this.route('path1',function(){ this.route('path2'); });});

如果想要设置初始页面,可以这样,这样就可以为/设置一个初始的路由:

Router.map(function() { this.route('index',{path:'/'});});

如果url中具有动态的参数呢?例如localhost:8000/path1/path2/2,这个2可能是一个id,又或者localhost:8000/path1/path2/2/edit这可能代表一个id为2的数据的编辑页面,那么在route.js可以如下表示:

Router.map(function() { this.route('path1', function(){ this.route('path2',{path:'/path2/:id'}); });});

通过这种方式,就可以进行访问,但是注意,以上的形式下localhost:8000/path1/path2是无法访问的。 在这种情况下,如何使用这个id参数呢?

//app/routes/path1/path2export default Ember.Route.extend({ model(params){ let id = params.id; return this.store.findRecord('model-example',id).then((data)=>{return data}); }});

如上就可以对参数进行使用。理论上来讲,是不能够修改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为:

<div class="header">这是上边栏</div><div class="sider">这是侧边栏</div><div class="content">{{outlet}}</div><div class="footer">这是下边栏</div>

那么渲染的时候,只会渲染到content中。 接下来我们讲解一下渲染的规则。这里这篇文章介绍的非常好Ember.js 入门指南之十四番外篇,路由、模板的执行、渲染顺序

渲染其他的模板也是允许的

一般情况下,我们要渲染的模板都是自己的哪一个,但是,ember也允许你渲染其他的模板,例如正常情况下,不做任何操作,path1/path2渲染的模板就是app/templates/path1/path2.hbs, 但是如果你想要进行更换,只需要重载方法renderTemplate()

//app/routes/path1/path2.jsexport default Ember.Route.extend({ renderTemplate() { this.render('path1/path2', { into: 'templateName', outlet: 'anOutletName' }); //this.render('path3');//这个语句可以使用path3替换path1/path2 }});

上面这个方法可以将path1/path2渲染到templateName 中使用{{outlet 'anOutletName'}}的地方。详情请见render。

model的执行顺序

model的执行顺序为从主到子。例如urlpath1/path2,那么model的执行顺序为application->path1->path2

模板的渲染顺序

首先,注意一点,就是,如果model执行完成后,才开始进行模板的渲染。 模板渲染的顺序为从子到主,也就是path2->path1->application->path1。渲染完成后,展示。这里需要全部渲染完后展示。

路由的重定向

路由重定向,例如,当权限控制等的时候,跳转到一个异常页面等,常常会用到,常用方法为transitionTo()transitionTo()的表现和helperlink-to的表现一致。另外还有一个方法replaceWith(),使用起来差不多。

transitionTo()可以在各个阶段使用:

//app/routes/path1.jsexport default Ember.Route.extend({ //在model初始化之前(发送请求之前) beforeModel(){ this.transitionTo('/path2'); }, //在初始化的时候, model(){ return this.findRecord('model-example',1).then((data)=>{ if(!data){ this.transitionTo('/path2'); }else{ return data; } }); } //在model初始化之后 afterModel(){ let model = this.modelFor('path1'); if(model.length){ this.transitionTo('/path2'); } }, actions:{ //可以在action中,比如点击后 transitionToPath2(){ this.transitionTo('/path2'); } }});

如果是要重定向到子路由,也可以直接在beforeModel() afterModel() model()中进行,但是如果这样的,考虑到子路由的执行过程,会再次将父路由执行一次,所有,ember有一个更加优化的方式redirect

//app/routes/path1.jsredirect(model, transition) { this.transitionTo('path1.path2');}

还有一些高级的用法,比如复杂url或者带参数的情况,注意这种情况下不能使用replaceWith进行替换:

//路由为 this.route('path2',{path:path2/:id});this.transitionTo('/path2/2');this.transitionTo('/path2',2);//同上{{link-to 'path2' 2}}//路由为 this.route('path2',{path:path2/:id}, function(){this.route('path3',{path:'path3/:p_id'})});this.transitionTo('/path2/2/paht3/3');{{link-to 'path2' 2 3}}//路由为this.route('path2',{path:path2/:id}); 且有参数`queryParams`(这个概念会在controller中说)this.transitionTo('/path2/2?params=1');this.transitionTo('/path2/2',{queryParams: { params: '1' }});//同上{{link-to 'path2' 2 (query-params params='1')}}

终止与重试路由跳转

有一些情况下,我们可能需要手动终止路由的跳转,或者是终止之后再重试。

当用户通过{{link-to}}、transition方法或者直接执行URL来转换路由,当前路由会自动执行willTransition方法。每个活动的路由都可以决定是否执行转换路由。 如果你正在填写一个表单,然后,无意点击了跳转,这样你的填写的数据就都丢失了,很不友好。所以一般会有一个确认页面。所以我们可以通过willTransition来先让用户确认是否离开再说。

// app/routes/path1.jsimport Ember from 'ember';export default Ember.Route.extend({ actions: { willTransition: function(transition) { if (!confirm("你确定要离开这个页面吗?")) { transition.abort(); } else { return true; } } }});

load 以及 error的处理

我们假设有这样的一个model:slow-model,这个model返回结果非常的慢,那么我们的以routedemo1/case1为例,如果route中加载情况如下:

model(){ return this.store.findAll('slow-model').then((data)=>{ return data; });}

那么,在slow-model返回结果之前,页面会一直是空白,这个非常不美观,我们一般会采用一个loading的图标或者有一个提示等。

Ember提供的解决办法是:在beforeModel、model、afterModel回调还没返回前先进入一个叫loading的子状态,然后渲染一个叫<your-route-name>-loading的模板(如果是application路由则对应的直接是loading、error不需要前缀)。

同样以demo1/path1为例:

//app/templates/application.hbs<p>application</p><p>application</p><p>application</p>{{outlet}}//app/routes/demo1.jsimport Ember from 'ember';export default Ember.Route.extend({ model() { return this.store.findAll('slow-model'); }})//app/templates/demo1.hbs<p>demo1</p><p>demo1</p><p>demo1</p>{{outlet}}//app/templates/demo1-loading.hbs<p>demo1-loading</p><p>demo1-loading</p><p>demo1-loading</p>//app/routes/demo1/case1.jsimport Ember from 'ember';export default Ember.Route.extend({ model() { return this.store.findAll('slow-model'); }//app/templates/demo1/case1.hbs<p>case1</p><p>case1</p><p>case1</p>//app/templates/demo1/case1-loading.hbs<p>case1-loading</p><p>case1-loading</p><p>case1-loading</p>

以上的代码,假设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,以分页为例:

//app/controllers/demo1/case1.jsimport Ember from 'ember';export default Ember.Controller.extend({ queryParams: ['pageSize','pageNum'], pageSize: null, pageNum:null});

这样就会在请求上带上这个参数,如果你没带的话,还是会取到这个参数,只不过都是null罢了。你可以为他们设置默认值:

//app/controllers/demo1/case1.jsimport Ember from 'ember';export default Ember.Controller.extend({ queryParams: ['pageSize','pageNum'], pageSize: 10, pageNum:1});

那么默认就是第一页的10个。注意,如果你传的值就是和默认的一样,在请求上是不会展示的!

关于查询参数的用法,跳转,在之前的transitionTo()方法以及link-to助手的的时候已经说过。

controller中还可以调用model的数据,以及使用计算属性,官网上给出一个例子:

import Ember from 'ember';export default Ember.Controller.extend({ queryParams: ['category'], category: null, filteredArticles: Ember.computed('category', 'model', function() { var category = this.get('category'); var articles = this.get('model'); if (category) { return articles.filterBy('category', category); } else { return articles; } })});

这样,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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表