摘要:文章來源模型,保存數據到數據庫環境搭建以及使用創建第一個靜態頁面引入計算屬性動態內容繼續為讀者介紹如何使用構建一個完整的復雜的項目。
文章來源:模型,保存數據到數據庫
環境搭建以及使用Ember.js創建第一個靜態頁面
引入計算屬性、action、動態內容
繼續為讀者介紹如何使用Ember構建一個完整的、復雜的項目。
第一個Ember.js模型在前面兩篇中實現了如何獲取界面輸入的郵箱值,但是并沒有真正保存到數據,僅僅只是獲取界面輸入的值并顯示出來。在本篇中將為讀者演示如何保存數據到數據庫中。但是我并不會去創建一個數據庫,而是使用firebase,更多有關firebase的信息請自行查閱資料學習(如果訪問firebase官網很慢或者是無法訪問那么你需要fanqiang)!
言歸正傳,回到Ember的模型介紹中來。簡單講Ember的模型其實就是一個與數據表對應的一個實體類,與Java中的JavaBean有點類似。
創建一個模型也非常簡單,可以直接使用Ember CLI命令創建,下面的命令就是用于創建模型類,并在模型中增加一個string類型的屬性email。
ember g model invitation email:string
命令執行完畢之后可以在項目對應目錄下看到創建的文件app/models/invitaction.js,文件內如如下:
// app/models/invitation.js import DS from "ember-data"; export default DS.Model.extend({ email: DS.attr("string") });
有了模型類之后修改控制器index.js的代碼,加入模型,通過模型來保存數據對象。
// app/controller/index.js import Ember from "ember"; export default Ember.Controller.extend({ headerMessage: "Coming Soon", responseMessage: "", // 設置默認值為空字符串 emailAddress: "", // 設置默認值為空字符串 // 使用正則表達式判斷郵箱格式,如果正確則返回true反之返回false isValid: Ember.computed.match("emailAddress", /^.+@.+..+$/), // 把計算屬性isValid綁定到isDisabled上 isDisabled: Ember.computed.not("isValid"), //當`disabled=false`時按鈕可用,所以正好需要取反 actions: { saveInvitation: function() { const email = this.get("emailAddress"); // 創建一個模型對象 const newInvitaction = this.store.createRecord("invitation", { email: email }); newInvitaction.save(); //保存模型對象到store中 this.set("responseMessage", `Thank you! We"ve just saved your email address: ${this.get("emailAddress")}`); // 情況輸入框內容 this.set("emailAddress", ""); } } });
等待項目重新啟動,在界面輸入正確的郵箱,點擊按鈕,可以在瀏覽器控制臺看到如下錯誤信息:
為何會出現這些錯誤呢??其實很簡單,我們并沒有對應的后臺服務區處理數據,目前僅僅是提交了數據而已,那么怎么處理呢?我們引入一個非常好用的數據庫——firebase。firebase為Ember提交了非常豐富的API,我們不需要再自己寫后臺的處理程序,可以直接調用firebase提供的API即可完成數據的CRUD操作。更多有關firebase的使用教程請看EmberFire Guide,在這個參考文檔上詳細介紹了firebase如何與Ember整合,Ember如何調用firebase提供的API。
下面簡單介紹如何把firebase整合到本項目中。
安裝 ember install emberfire,會自動創建文件app/adapters/application.js
在firebase官網注冊一個用戶,并創建一個APP(如下圖1位置創建APP)然后會得到一個管理連接(比如:luminous-xxx-xxx.firebaseIO.com)
修改項目中config/environment.js,在文件中增加firebase的配置。
圖1
config/environment.js配置代碼如下:
var ENV = { modulePrefix: "library-app", environment: environment, contentSecurityPolicy: { "connect-src": ""self" https://auth.firebase.com wss://*.firebaseio.com" }, firebase: "https://YOUR-FIREBASE-NAME.firebaseio.com/", //改成自己在firebase上APP的地址 baseURL: "/", locationType: "auto", EmberENV: { FEATURES: { // Here you can enable experimental features on an ember canary build // e.g. "with-controller": true } }, APP: { // Here you can pass flags/options to your application instance // when it is created } }; // ……其他代碼省略
注意上述代碼中的第5行,firebase屬性的值是自己在firebase申請的APP的URL。一定記得要修改!!!
修改完成之后手動重啟項目,記得是手動關閉終端運行的項目(ctrl+c關閉),然后再使用命令ember s啟動項目。否則新安裝的emberfire無法起作用。
等待項目啟動完成,如果啟動過程中沒有出現錯誤,說明emberfire安裝成功!
然后激動的時刻到了,在首頁輸入正確的郵箱,點擊按鈕,可以看到瀏覽器控制來不會報錯了!并且在firebase官網的APP中看到剛剛新增的郵箱!!
注意:點擊按鈕提交后可能看到界面沒有任何反應,先別急,由于firebase是外國的東西,在天朝訪問都是比較慢,你懂的。提交后到響應回來可能比較慢。
從瀏覽器控制臺打印的日志可以看出向firebase發送請求,截圖如下:
并且在界面上提示了保存成功的信息!
最后在firebase官網上可以查看到剛剛提交數據。
可以感受到firebase的強大了吧!我們幾乎沒有做任何處理數據就直接保存到firebase了,并且會自動根據模型創建數據,不過需要注意的是我們在模型定義中并不需要定義id屬性,firebase會自動生成一個唯一的id屬性值,截圖中的-KEr3XwUQjgLjb5yx0dp就是id屬性值。
到此,數據的保存工作完成了,借助firebase大大簡化了自己需要處理的東西,不需要自己創建數據庫、數據表、以及保存數據sql等等!不知道你是否看明白了,如果有疑問請及時給我留言,我會盡力為你解答!
promise和thispromise(承諾)在JavaScript中是一個異步特性。這個特性還在完善之中,更多有關promise的介紹請看promises-book或者Mozilla MDN Promise。
在前面保存數據的代碼中save()方法返回值就是一個promise,我們可以根據save()方法的返回值做不同的處理,比如保存失敗時候的處理。
saveInvitation: function() { const email = this.get("emailAddress"); // 創建一個模型對象 const newInvitaction = this.store.createRecord("invitation", { email: email }); //保存模型對象到store中 newInvitaction.save().then(function(msg) { console.log("保存成功。"); }, function(reason) { console.log("保存失敗!"); }); this.set("responseMessage", `Thank you! We"ve just saved your email address: ${this.get("emailAddress")}`); // 情況輸入框內容 this.set("emailAddress", ""); }
如果你看過有關promise的介紹那么理解上述代碼應該是很簡單的,在方法then()中第一個函數(參數)會在save()執行成功的時候執行,第二個函數(參數)會在save()執行失敗的時候執行。明白這個之后我們再修改控制器index.js的代碼。我們把提示信息放在save()執行成功的時候執行方法中。
saveInvitation: function() { const email = this.get("emailAddress"); // 創建一個模型對象 const newInvitaction = this.store.createRecord("invitation", { email: email }); //保存模型對象到store中 newInvitaction.save().then(function(msg) { this.set("responseMessage", `Thank you! We"ve just saved your email address: ${this.get("emailAddress")}`); // 情況輸入框內容 this.set("emailAddress", ""); }, function(reason) { this.set("responseMessage", `Saved: ${this.get("emailAddress")} failed!`); // 情況輸入框內容 this.set("emailAddress", ""); }); }
等待項目自動重啟完成,在界面輸入正確郵箱,提交數據,此時并沒有出現任何反應,并且會在瀏覽器控制臺看到如下錯誤,
這又是什么原因呢?其實原因很簡單,因為this作用域問題,由于是在then()內部使用了this導致此時的this指向的并不是控制器類了,只有在Ember的上下文中才能使用set()方法!我們用一個臨時變量解決這個問題,代碼修改為如下:
// app/controller/index.js import Ember from "ember"; export default Ember.Controller.extend({ headerMessage: "Coming Soon", responseMessage: "", // 設置默認值為空字符串 emailAddress: "", // 設置默認值為空字符串 // 使用正則表達式判斷郵箱格式,如果正確則返回true反之返回false isValid: Ember.computed.match("emailAddress", /^.+@.+..+$/), // 把計算屬性isValid綁定到isDisabled上 isDisabled: Ember.computed.not("isValid"), //當`disabled=false`時按鈕可用,所以正好需要取反 actions: { saveInvitation: function() { const email = this.get("emailAddress"); // 創建一個模型對象 const newInvitaction = this.store.createRecord("invitation", { email: email }); var _this = this; //保存模型對象到store中 newInvitaction.save().then(function(msg) { _this.set("responseMessage", `Thank you! We"ve just saved your email address: ${_this.get("emailAddress")}`); // 情況輸入框內容 _this.set("emailAddress", ""); }, function(reason) { _this.set("responseMessage", `Saved: ${_this.get("emailAddress")} failed!`); // 情況輸入框內容 _this.set("emailAddress", ""); }); } } });
等待項目自動重啟完成,在頁面輸入正確的郵箱并提交,可以看到此時效果與之前是一樣的,然后去firebase查看結果,也是可以看到新增的數據。
雖然是用臨時變量方式可以解決由于this作用域問題,但是還有更加優美的解決辦法,如今幾乎所有新版的瀏覽器引擎已經支持ES2015,可以使用ES2015的=>操作符解決this作用域問題,請看下面的處理代碼:
saveInvitation: function() { const email = this.get("emailAddress"); // 創建一個模型對象 const newInvitaction = this.store.createRecord("invitation", { email: email }); //保存模型對象到store中 newInvitaction.save().then((response) => { console.log("response = " + response); this.set("responseMessage", `Thank you! We"ve just saved your email address: ${response.get("id")}`); // 情況輸入框內容 this.set("emailAddress", ""); }, (reason) => { this.set("responseMessage", `Saved: ${this.get("emailAddress")} failed!`); // 情況輸入框內容 this.set("emailAddress", ""); }); }
使用ES2015的特性之后不僅解決了this作用域問題,而且連關鍵字function都不需要了,使用=>操作會自動把外層this所指的對象傳遞到函數內部,并且修改了保存成功時的提示信息,使用${response.get("id")}從firebase響應的數據中獲取到保存成功后返回的id值,返回的response就是一個模型invitation的對象,可以使用get()方法獲取對象值。
再次測試,如果項目代碼沒有誤那么你可以得到如下截圖的提示信息(id值跟你的是不一樣的),
如果你對this不是很懂,請看認真看下面文章的解釋:
Mozilla MDN this
Javascript的this用法
創建管理頁面前面已經介紹了如何整合firebase到項目中,并且已經成功保存增加的數據。可以在firebase上看到所有數據,我們創建一個后臺頁面去管理這些數據。
下面創建一個子路由和路由對應的模板頁面,仍然是使用Ember CLI命令創建,命令如下:
ember g route admin/invitaction
命令執行完畢后會得到一個路由文件(app/routes/admin/invitaction.js)和一個模板文件(app/templates/admin/invitaction.hbs),命令會自動創建文件夾admin,子路由和子模板會放在子子目錄下。
然后在首頁增加菜單鏈接,修改navbar.hbs模板。
代碼{{#link-to "admin.invitation" tagName="li"}}Invitations{{/link-to}}中admin.invitation是一個嵌套路由或者說是子路由。更多有關路由嵌套問題請看Ember.js 入門指南之十三{{link-to}} 助手。
在模板中使用表格遍歷顯示所有的郵箱數據。修改模板invitaction.hbs。
Invitations
ID | |
---|---|
{{invitation.id}} | {{invitation.email}} |
上述代碼中{{#each}}{{/each}}是Ember提供的遍歷表達式,此表達式用于遍歷數組數據。本例子中用戶遍歷從路由的model回調中返回的數據。更多有關此表達式的介紹請看Ember.js 入門指南之十handlebars遍歷標簽。
修改路由app/routes/admin/invitations.js在model回調中獲取服務器(firebase)上的數據。
// app/routes/admin/invitations.js import Ember from "ember"; export default Ember.Route.extend({ model() { return this.store.findAll("invitation"); } });
等待項目重啟完成,可以在項目首頁導航欄的右側看到可以點擊下拉的Admin菜單項,點擊菜單看到子菜單項“Invitation”,點擊“Invitation”進入到http://localhost:4200/admin/invitation。
在界面上可以看到之前新增的所有郵箱信息和firebase自動生成的ID屬性值(由于firebase是老外的東西獲取數據會比較慢,數據顯示自然也會比較慢,稍等一會就在界面上看到了!)。如果你項目代碼沒問題也可以看到如下的截圖。
到這一步我們已經可以完整的從服務器獲取數據,并以列表形式顯示在界面上。本教程的目標是創建一個簡單的圖書管理系統,前面的文章已經完成了類似于用戶注冊的功能,接下來我們創建一個library模型,用于保存書庫信息。
同樣是使用Ember CLI命令創建模型。
ember g model library name:string address:string phone:string
上述命令創建了一個包含三個屬性的模型,這三個屬性都是string類型的數據。創建完模型之后再繼續創建三個模板,分別用戶顯示library列表新建library數據。
ember g template libraries ember g template libraries/index ember g template libraries/new
模板創建完畢之后手動在router.js中增加路由配置,這次我們不采用Ember CLI命令創建了!!!
// app/router.js import Ember from "ember"; import config from "./config/environment"; var Router = Ember.Router.extend({ location: config.locationType }); Router.map(function() { this.route("about"); this.route("contact"); this.route("admin", function() { this.route("invitation"); }); this.route("libraries", function() { this.route("new"); }); }); export default Router;
再更新首頁模板navbar.hbs,增加一個菜單項“libraries”,其他代碼不變。
修改模板libraries.hbs,增加菜單鏈接。
Libraries
{{outlet}}
等待項目重啟完成,進入http://localhost:4200/libraries。可以看到如下圖的界面
此時點擊界面的上的“List all”和“Add new”除了看到URL變化之外還沒任何效果,因為我們的子模板libraries/index.hbs、libraries/new.hbs還沒有任何內容,下面在這兩個模板中增加一些代碼。
List
{{#each model as |library|}}{{/each}}{{library.name}}
Address: {{library.address}}
Phone: {{library.phone}}
Add a new local Library
{{input type="text" value=model.name class="form-control" placeholder="The name of the Library"}}{{input type="text" value=model.address class="form-control" placeholder="The address of the Library"}}{{input type="text" value=model.phone class="form-control" placeholder="The phone number of the Library"}}
模板new.hbs是一個表單,用于新增數據,通過點擊按鈕“Add to library list”提交表單數據,表單數據由路由libraries/new.js中的saveLibrary方法處理,此時此方法還沒定義,在接下來的代碼中會定義。
在{{action}}表達式中傳遞了一個參數model到處理的后臺,表單中的其他屬性會以model的屬性方式傳遞到后臺,之所以可以這樣做是因為在模板對應的路由中返回了一個空的library對象,在接下來的路由libraries/new.js將看到。
等待項目重啟完,在點擊“List all”和“Add new”可以看到這兩個子模板的內容渲染到父模板libraries.hbs的{{outlet}}上。不過由于并沒有在路由中獲取模型library的數據所以“List all”頁面還沒有任何數據,“Add new”頁面是第一個新增數據吧表單。
下面在路由libraries/index.js中獲取library的數據,并在model回調中返回到模板中遍歷顯示。
使用Ember CLI命令創建路由,創建過程會詢問是否覆蓋已經存在的模板文件,輸入n選擇否。
ember g route libraries/index ember g route libraries/new
路由創建完成之后分別在這兩個路由中增加獲取數據的代碼。
// app/routes/libraries/index.js import Ember from "ember"; export default Ember.Route.extend({ model() { return this.store.findAll("library"); } });
// app/routes/libraries/new.js import Ember from "ember"; export default Ember.Route.extend({ model() { return this.store.createRecord("library"); }, actions: { // 處理模板上輸入的數據 saveLibrary(newLibrary) { newLibrary.save().then(() => this.transitionTo("libraries")); }, willTransition() { // rollbackAttributes() removes the record from the store // if the model "isNew" this.controller.get("model").rollbackAttributes(); } } });
在此路由的model回調中我們創建了一個空的library對象,并返回到模板頁面。這也是為什么能在模板中傳遞參數model的原因。
代碼中方法willTransition()是Ember提供的內置方法,此方法的作用是當用戶離開當前URL時會清空未保存到服務器的library數據,如果不重置modelEmber會在路由切換的時候自動保存數據到服務器上。
保存數據的方法saveLibrary()寫的比較簡潔,它的作用是:先調用save()方法保存數據,如果保存成功在調用方法transitionTo()跳轉到路由libraries下(library首頁),有關=>語法的介紹請看Mozill MDN Arrow_functions。
在上述代碼中多次是用了this.controller,但是在路由中并沒有這個屬性controller而且也沒有控制器文件app/controllers/libraries/new.js,運行項目并不會報錯!這是為什么呢?這是因為Ember會自動生成一個虛擬的控制器并在保存在內存中,根據Ember的命名規則會自動生成一個與路由同名的控制器,
為了驗證這個說法,打開瀏覽器的控制臺,在打開標簽“Ember”然后點擊左側的“/# Routes”,找到路由libraries這一塊,可以看到如下截圖的信息。
可以看到每個路由都對應著一個同名的控制器。
等待項目重啟完畢,開始驗證代碼是否實現了所設想的要求。
首先在新增頁面輸入如下截圖信息,然后點擊按鈕保存數據。
稍等片刻,等待數據保存完畢,可以看到界面順利跳轉到了http://localhost:4200/libraries下,如下圖所示,并且看到了剛剛新增的數據,為了驗證數據是否真的保存到服務器中,我們進入到firebase的APP中查看,可以看到數據以及保存到里library下。
library數據列表頁面截圖
firebase上保存的library數據截圖
此時,如果你注釋了方法willTransition()結果會是怎么樣的呢!!如果沒有這個方法去重置model,當你每次在“Add new”頁面輸入輸入并且沒有點擊“Add to library list”保存數,然后切換到其他路由下(比如點擊“List all”切換到路由libraries下)會自動保存一條數據到服務器。
在“Add new”頁面輸入如下截圖數據
點擊按鈕“List all”切換到路由libraries下,可以看到在“Add new”頁面添加的數據,如下圖所示,但是如果你手動刷新頁面后可以發現這條數據不見了,并且在firebase上也沒有這條數據,可見這條數據僅僅是保存到Ember的store中,并沒有真正保存到服務器上。這樣的體驗是非常糟糕的!!
其中,實現重置model的方式還有另外一種更加合適的方法,代碼如下:
willTransition() { // rollbackAttributes() removes the record from the store // if the model "isNew" // this.controller.get("model").rollbackAttributes(); let model = this.controller.get("model"); if (model.get("isNew")) { model.destroyRecord(); } }
本篇的內容到此全部介紹完畢!本篇我們實現了數據的保存、顯示,特別是library數據的保存。數據的保存、顯示都需要與模型關聯,模型在Ember是一個非常重要的內容!希望讀者好好掌握模型。
家庭作業下面兩個作業完成其中之一即可。本篇選擇第一個,第二個請讀者自行完成!動手才是真理。
增強contact的功能創建一個包括兩個屬性email和message的模型contact,參考代碼
修改路由app/routes/contact.js返回一個空對象到模板上,參考代碼
修改控制器contact.js,保存數據到firebase,參考代碼
把放在控制器中的校驗代碼移動到模型app/models/contact.js中,參考代碼
創建一個管理contact的后臺頁面http://localhost:4200/admin/contacts,參考代碼
在項目首頁的導航菜單上增加一個菜單項“Contacts”,點擊菜單進入http://localhost:4200/admin/contact,參考代碼
使用表格展示所有的contact數據,參考代碼
使用路由和模型重構有關contact的代碼把有關contact的檢驗放到模型類中
把控制器contact.js中保存數據的代碼移動到同名的路由中
測試通過后刪除控制器contact.js
作業演示結果
當輸入的郵箱格式正確,并且message長度大于6時按鈕“send”才可用。
保存成功后情況輸入框,并且顯示提示信息。當切換路由后再進入到http://localhost:4200/contact會清空保存成功的提示信息。
后臺頁面成功顯示了新增的數據,即使手動刷新頁面數據也不會丟失,可見數據已經保存到firebase,在此不再貼firebase上的數據截圖了!
為了照顧懶人我把完整的代碼放在GitHub上,如有需要請參考參考。博文經過多次修改,博文上的代碼與github代碼可能有出入,不過影響不大!如果你覺得博文對你有點用,請在github項目上給我點個star吧。您的肯定對我來說是最大的動力!!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79521.html
摘要:交易依然表示狀態的變化遷移。這樣一個模型的特點是狀態是第一性的所有者是狀態的屬性,每一份狀態只有一個所有者狀態不斷的被銷毀和創建。值得指出的是,的編程模型之所以強大,更多是因為它有狀態,而不是因為它的有限圖靈完備。 在設計 CKB 的時候,我們想要解決三個方面的問題: 狀態爆炸引起的公地悲劇及去中心化的喪失;計算和驗證耦合在了一起使得無論是計算還是驗證都失去了靈活性,難以擴展;交易與價...
摘要:為了理解底層公鏈的模型,我們前置了幾篇概念性文章,講述了我們應該以狀態為中心設計區塊鏈系統的,以及這么做帶來的好處。交易依然表示狀態的變化遷移。 為了理解底層公鏈 CKB 的 Cell 模型,我們前置了幾篇概念性文章,講述了我們應該以狀態為中心設計區塊鏈系統的,以及這么做帶來的好處。并且在上一篇文章中,詳細分析了比特幣 UTXO 模型和以太坊的 Account 模型,以及進行了對比分析...
摘要:二在中函數是如何執行的函數我們之前已經都接觸過了,函數無非有兩部分數據和對數據的操作。函數的調用輸出結果為我們使用了的調試,函數在執行時會將參數和函數中所用到的變量方法相同的地位,即在函數內部執行的時候不會區分是參數還是變量。 showImg(https://segmentfault.com/img/remote/1460000017790532); 準備寫點亂七八糟的文章,對Java...
好的,下面是一篇關于使用CPU訓練TensorFlow的編程技術文章: TensorFlow是一種非常流行的機器學習框架,它可以用于訓練各種深度學習模型。雖然通常使用GPU進行訓練,但在某些情況下,使用CPU進行訓練可能更加適合。本文將介紹如何使用CPU訓練TensorFlow,并提供一些編程技巧。 1. 確認TensorFlow版本 首先,您需要確認您正在使用的TensorFlow版本是否...
摘要:使用模型的代表是比特幣。每一個比特幣全節點都會維護當前所有的集合,這個集合我們就稱為比特幣賬本的當前狀態即當前的賬本。 本文是對前幾期秘猿區塊鏈課堂中關于 Cell 模型的總結。底層公鏈 CKB 的 Cell 模型是一個高度抽象的模型,事實上,你不僅可以在 Cell 上實現 First-class Asset,也可以在 Cell 上模擬 Account。通過本文的介紹我們可以看出,Ce...
閱讀 2413·2021-11-18 10:02
閱讀 1927·2021-10-13 09:40
閱讀 3006·2021-09-07 10:07
閱讀 2114·2021-09-04 16:48
閱讀 1014·2019-08-30 13:18
閱讀 2461·2019-08-29 14:03
閱讀 2927·2019-08-29 12:54
閱讀 3163·2019-08-26 11:41