在前面的Ember.js 入門指南之二十路由定義提過loading
、error
子路由,它們是Ember默認創(chuàng)建的,并在beforeModel
、model
、afterModel
這三個回調(diào)執(zhí)行完畢之前會先渲染當(dāng)前路由的loading
和error
模板。
Router.map(function() {
this.route('posts', function() {
this.route('post', { path: '/:post_id'});
});
});
對于上述的路由設(shè)置Ember會生成如下的路由列表:
每個路由都會自動生成一個loading
、error
路由,下面我將一一演示這兩個路由的作用。
圖片前面loading
、error
路由對應(yīng)的application
路由。posts_loading
和posts_error
對應(yīng)的是posts
路由。
Ember建議數(shù)據(jù)放在beforeModel
、model
、afterModel
回調(diào)中獲取并傳遞到模板顯示。但是只要是加載數(shù)據(jù)就需要時間,對于Ember應(yīng)用來說,在model
等回調(diào)中加載完數(shù)據(jù)才會渲染模板,如果加載數(shù)據(jù)比較慢那么用戶看到的頁面就是一個空白的頁面,用戶體驗很差!
Ember提供的解決辦法是:在beforeModel
、model
、afterModel
回調(diào)還沒返回前先進入一個叫loading
的子狀態(tài),然后渲染一個叫routeName-loading
的模板(如果是application
路由則對應(yīng)的直接是loading
、error
不需要前綴)。
為了演示這效果在app/templates
下創(chuàng)建一個posts-loading
模板。如果程序正常,在渲染模板posts
之前會先渲染這個模板。
<img src="assets/images/loading/loading.gif" />
然后修改路由posts.js
,讓model
回調(diào)執(zhí)行時間更長一些。
// app/routes/posts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
// 模擬一個延時操作,
for (var i = 0; i < 10000000;i++) {
}
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls');
}
});
執(zhí)行http://localhost:4200/posts,首先會看到執(zhí)行的loading
模板的內(nèi)容,然后才看到真正要顯示的數(shù)據(jù)。有一個加載過程,如下面2幅圖片所示。
在beforeModel
、model
、afterModel
回調(diào)沒有立即返回之前,會先執(zhí)行一個名為loading的事件。
// app/routes/posts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
// 模擬一個延時操作,
for (var i = 0; i < 10000000;i++) {
}
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls');
},
actions: {
loading: function(transition, originRoute) {
alert("Sorry this is taking so long to load!!");
}
}
});
頁面刷新后會彈出一個提示框,先不點擊“確定”。打開瀏覽器的“開發(fā)者 -> 開發(fā)者工具”,切換到Network標(biāo)簽下。找到“pulls”這個請求,點擊它。
從圖中可以看到此時model
回調(diào)并沒有返回。此時響應(yīng)的內(nèi)容是空的,說明loading
事件實在model
回調(diào)返回之前執(zhí)行了。
然后點擊彈出框的“確定”,此時可以看到Response有數(shù)據(jù)了。說明model
回調(diào)已經(jīng)執(zhí)行完畢。
注意:如果當(dāng)前的路由沒有顯示定義loading
事件,這個時間會冒泡到父路由,如果父路由也沒有顯示定義loading
事件,那么會繼續(xù)向上冒泡,一直到最頂端的路由application
。
與loading
子狀態(tài)類似,error
子狀態(tài)會在beforeModel
、model
、afterModel
回調(diào)執(zhí)行過程中出現(xiàn)錯誤的時候觸發(fā)。
命名方式與loading
子狀態(tài)也是類似的?,F(xiàn)在定義一個名為posts-error.hbs
的模板。
<p style="color: red;">
posts回調(diào)解析出錯。。。。
</p>
然后在model
回調(diào)中手動添加一個錯誤代碼。
// app/routes/posts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
// 模擬一個延時操作,
for (var i = 0; i < 10000000;i++) {
}
var e = parseInt(value);
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls');
}
});
注意var e = parseInt(value);
這句代碼,由于value
沒有定義所以應(yīng)該會報錯。那么此時頁面會顯示什么呢??
如果你的演示程序沒有其他問題那么你也會得到上圖的結(jié)果。但是如果沒有定義這個模板,那么界面上將是什么都不顯示。
如果你想在xxx-error.hbs
模板上看到是什么錯誤信息,你可以在模板上打印model
的值。如下:
<p style="color: red;">
posts回調(diào)解析出錯。。。。
<br>
{{model}}
</p>
此時頁面會顯示出你的代碼是什么錯誤。
不過相比于瀏覽器控制臺打印的錯誤信息簡單很多!??!
error
事件與第一點講的loading事件也是相似的。使用方式與loading
一樣。個人覺得這個事件非常有用,我們可以在這個事件中根據(jù)error
狀態(tài)碼的不同執(zhí)行不同的邏輯,比如跳轉(zhuǎn)到不同的路由上。
// app/routes/posts.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return Ember.$.getJSON('https://api.github.com/repos/emberjs/ember.js/pulls____');
},
actions: {
error: function(error, transition) {
console.log('error = ' + error.status);
// 打印error對象里的所有屬性和方法名
for(var name in error){
console.log(name);
// console.log('屬性值或者方法體==》' + error[name]);
}
alert(names);
if (error && error.status === 400) {
return this.transitionTo("about");
} else if (error.status === 404) {
return this.transitionTo("form");
} else {
console.log('else......');
}
}
}
});
注意getJSON
方法里的URL,我在URL后面隨機加了一些字符,目的是讓這個URL不存在。此時請求應(yīng)該會找不到這個地址error
的響應(yīng)碼應(yīng)該是404。然后直接跳轉(zhuǎn)到form
這個路由上。
運行http://localhost:4200/posts之后,瀏覽器控制臺打印信息如下:
頁面也會跳轉(zhuǎn)到form
。
到此路由加載數(shù)據(jù)過程中涉及的兩個狀態(tài)loading
和error
的內(nèi)容全部介紹完,這兩個狀態(tài)在優(yōu)化用戶體驗方面是非常有用的,希望想學(xué)習(xí)Ember的同學(xué)好好掌握?。?!=^=
博文完整代碼放在Github(博文經(jīng)過多次修改,博文上的代碼與github代碼可能又出入,不過影響不大!),如果你覺得博文對你有點用,請在github項目上給我點個star
吧。您的肯定對我來說是最大的動力??!
更多建議: