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
新聞熱點
疑難解答