摘要:創(chuàng)建模型并設(shè)置關(guān)聯(lián)關(guān)聯(lián)關(guān)系設(shè)置模型關(guān)系一個對應(yīng)多個,一個對應(yīng)多個。手動在中增加關(guān)聯(lián)關(guān)系。并且是實現(xiàn)了數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系,比如一個對應(yīng)多個,如下圖。
文章來源:模型高級特性,引入模型關(guān)聯(lián)關(guān)系
接著前面五篇:
環(huán)境搭建以及使用Ember.js創(chuàng)建第一個靜態(tài)頁面
引入計算屬性、action、動態(tài)內(nèi)容
模型,保存數(shù)據(jù)到數(shù)據(jù)庫
發(fā)布項目,加入CRUD功能
從服務(wù)器獲取數(shù)據(jù),引入組件
前言本篇主要是介紹模型直接的關(guān)聯(lián)關(guān)系,比如:一對一、一對多關(guān)系。會創(chuàng)建兩個模型author和book,設(shè)置它們的關(guān)系,并增加測試數(shù)據(jù)。
創(chuàng)建模型并設(shè)置關(guān)聯(lián)關(guān)聯(lián)關(guān)系設(shè)置API:
belongsTo
hasMany
模型關(guān)系:一個library對應(yīng)多個book,一個author對應(yīng)多個book。關(guān)系圖如下:
使用Ember CLI命令創(chuàng)建模型。
ember g model book title:string releaseYear:date library:belongsTo author:belongsTo ember g model author name:string books:hasMany
手動在library中增加hasMany關(guān)聯(lián)關(guān)系。
import Model from "ember-data/model"; import attr from "ember-data/attr"; import { hasMany } from "ember-data/relationships"; import Ember from "ember"; export default Model.extend({ name: attr("string"), address: attr("string"), phone: attr("string"), books: hasMany("books"), isValid: Ember.computed.notEmpty("name"), });創(chuàng)建一個后臺管理頁面“Seeder”
ember g route admin/seeder
檢查router.js看看路由是否成功創(chuàng)建。相關(guān)代碼如下:
// 其他代碼不變,省略 this.route("admin", function() { this.route("invitations"); this.route("contacts"); this.route("seeder"); }); // 其他代碼不變,省略
修改導航模板navbar.hbs增加新建路由的入口鏈接。
使用Ember.RSVP.hash()在一個路由中返回多個模型的數(shù)據(jù)Ember支持在一個路由的model回調(diào)中返回多個模型的數(shù)據(jù)。有關(guān)方法發(fā)API請看Ember.RSVP.hash()。
// app/routes/admin/seeder.js import Ember from "ember"; export default Ember.Route.extend({ model() { return Ember.RSVP.hash({ libraries: this.store.findAll("library"), books: this.store.findAll("book"), authors: this.store.findAll("author") }) }, setupController(controller, model) { controller.set("libraries", model.libraries); controller.set("books", model.books); controller.set("authors", model.authors); } });
上述model()回調(diào)中返回了三個模型的數(shù)據(jù):library、book和author。需要注意的是:上述代碼中方法Ember.RSVP.hash()會發(fā)送3個請求,并且只有三個請求都成功才會執(zhí)行成功。
在setupController()回調(diào)中,把三個模型分別設(shè)置到controller中。
每個路由內(nèi)都內(nèi)置了很多方法,比如前面介紹的model、setupController、renderTemplate,這些都是內(nèi)置在路由類中的方法,那么這些方法調(diào)用次序又是如何的呢?請看下面的代碼:
// app/routes/test.js import Ember from "ember"; export default Ember.Route.extend({ init() { debugger; }, beforeModel(transition) { debugger; }, model(params, transition) { debugger; }, afterModel(model, transition) { debugger; }, activate() { debugger; }, setupController(controller, model) { debugger; }, renderTemplate(controller, model) { debugger; } });
打開瀏覽器的debug模式并在執(zhí)行到這個路由中http://localhost:4200/test??梢钥吹椒椒ǖ膱?zhí)行次序與上述代碼方法的次序是一致的。有關(guān)API請看下面網(wǎng)址的介紹:
init()
beforeModel(transition)
model(params, transition)
activate()
setupController(controller, model)
renderTemplate(controller, model)
數(shù)量顯示功能創(chuàng)建一個組件用于顯示各個模型數(shù)據(jù)的總數(shù)。
ember g component number-box
組件創(chuàng)建完畢之后在組件類中增加css類,使用屬性classNames設(shè)置。
// app/components/number-box.js import Ember from "ember"; export default Ember.Component.extend({ classNames: ["panel", "panel-warning"] });
然后在組件模板中增加代碼:
{{title}}
{{if number number "..."}}
在修改app/templates/admin/seeder.hbs
Seeder, our Data Center
{{number-box title="Libraries" number=libraries.length}}{{number-box title="Authors" number=authors.length}}{{number-box title="Books" number=books.length}}
等待項目重啟完成,進入到后臺的seeder下可以看到三個小圓點,請記得,一定要在setupController中設(shè)置數(shù)據(jù),model回調(diào)會自動從服務(wù)器獲取數(shù)據(jù),obj.length意思是調(diào)用length()方法獲取數(shù)據(jù)長度,然后直接顯示到模板上,效果如下截圖,由于后面兩個模型還沒有數(shù)據(jù)所以顯示省略號。
構(gòu)建表單生成測試數(shù)據(jù)前面已經(jīng)介紹過屬性的傳遞,下面的代碼將為讀者介紹一些更加高級的東西??!一大波代碼即將來臨?。?!
ember g component seeder-block ember g component fader-label
// app/components/seeder-block.js import Ember from "ember"; export default Ember.Component.extend({ actions: { generateAction() { this.sendAction("generateAction"); }, deleteAction() { this.sendAction("deleteAction"); } } });
{{sectionTitle}}
{{input value=counter class="form-control"}}{{#fader-label isShowing=createReady}}Created!{{/fader-label}}{{#fader-label isShowing=deleteReady}}Deleted!{{/fader-label}}
// app/components/fader-label.js import Ember from "ember"; export default Ember.Component.extend({ tagName: "span", classNames: ["label label-success label-fade"], classNameBindings: ["isShowing:label-show"], isShowing: false, isShowingChanged: Ember.observer("isShowing", function() { Ember.run.later(() => { this.set("isShowing", false); }, 3000); }) });
代碼 classNames: ["label label-success label-fade"]的作用是綁定三個CSS類到標簽span上,得到html如xxx。
代碼classNameBindings: ["isShowing:label-show"]的作用是根據(jù)屬性isShowing的值判斷是否添加CSS類label-show到標簽span上。更多有關(guān)信息請看Ember.js 入門指南之十二handlebars屬性綁定
{{yield}}
// app/styles/app.scss @import "bootstrap"; body { padding-top: 20px; } html { overflow-y: scroll; } .library-item { min-height: 150px; } .label-fade { opacity: 0; @include transition(all 0.5s); &.label-show { opacity: 1; } }
最主要、最關(guān)鍵的部分來了。
Seeder, our Data Center
{{seeder-block sectionTitle="Libraries" counter=librariesCounter generateAction="generateLibraries" deleteAction="deleteLibraries" createReady=libDone deleteReady=libDelDone }} {{seeder-block sectionTitle="Authors with Books" counter=authorCounter generateAction="generateBooksAndAuthors" deleteAction="deleteBooksAndAuthors" createReady=authDone deleteReady=authDelDone }}{{number-box title="Libraries" number=libraries.length}}{{number-box title="Authors" number=authors.length}}{{number-box title="Books" number=books.length}}
屬性generateAction和deleteAction用于關(guān)聯(lián)控制器中的action方法,屬性createReady和deleteReady是標記屬性。
等待項目重啟完畢,頁面結(jié)果如下:
底部的兩個輸入框用于獲取生成的數(shù)據(jù)條數(shù)。
安裝faker.js構(gòu)建測試數(shù)據(jù)使用faker.js構(gòu)建測試數(shù)據(jù)。
ember install ember-faker
安裝完畢之后擴展各個模型,并在模型中調(diào)用randomize()方法產(chǎn)生數(shù)據(jù)。下面是各個模型的代碼。
// app/models/library.js import Model from "ember-data/model"; import attr from "ember-data/attr"; import { hasMany } from "ember-data/relationships"; import Ember from "ember"; import Faker from "faker"; export default Model.extend({ name: attr("string"), address: attr("string"), phone: attr("string"), books: hasMany("book", {inverse: "library", async: true}), isValid: Ember.computed.notEmpty("name"), randomize() { this.set("name", Faker.company.companyName() + " Library"); this.set("address", this._fullAddress()); this.set("phone", Faker.phone.phoneNumber()); // If you would like to use in chain. return this; }, _fullAddress() { return `${Faker.address.streetAddress()}, ${Faker.address.city()}`; } });
// app/models/book.js import Model from "ember-data/model"; import attr from "ember-data/attr"; import { belongsTo } from "ember-data/relationships"; import Faker from "faker"; export default Model.extend({ title: attr("string"), releaseYear: attr("date"), author: belongsTo("author", {inverse: "books", async: true}), library: belongsTo("library", {inverse: "books", async: true}), randomize(author, library) { this.set("title", this._bookTitle()); this.set("author", author); this.set("releaseYear", this._randomYear()); this.set("library", library); return this; }, _bookTitle() { return `${Faker.commerce.productName()} Cookbook`; }, _randomYear() { return new Date(this._getRandomArbitrary(1900, 2015)); }, _getRandomArbitrary(min, max) { return Math.random() * (max - min) + min; } });
// app/models/author.js import Model from "ember-data/model"; import attr from "ember-data/attr"; import { hasMany } from "ember-data/relationships"; import Faker from "faker"; export default Model.extend({ name: attr("string"), books: hasMany("book", {inverse: "author", async: true}), randomize() { this.set("name", Faker.name.findName()); return this; } });
上述代碼中。 async設(shè)置為true的作用是:在獲取book的同時會把關(guān)聯(lián)的author也加載出來,默認是不加載(延遲加載)。
// app/controllers/admin/seeder.js import Ember from "ember"; import Faker from "faker"; export default Ember.Controller.extend({ libraries: [], books: [], authors: [], actions: { generateLibraries() { const counter = parseInt(this.get("librariesCounter")); for (let i = 0; i < counter; i++) { this.store.createRecord("library").randomize().save().then(() => { if (i === counter-1) { this.set("librariesCounter", 0); this.set("libDone", true); } }); } }, deleteLibraries() { this._destroyAll(this.get("libraries")); this.set("libDelDone", true); }, generateBooksAndAuthors() { const counter = parseInt(this.get("authorCounter")); for (let i = 0; i < counter; i++) { let newAuthor = this.store.createRecord("author"); newAuthor.randomize() .save().then(() => { if (i === counter-1) { this.set("authorCounter", 0); this.set("authDone", true); } } ); this._generateSomeBooks(newAuthor); } }, deleteBooksAndAuthors() { this._destroyAll(this.get("books")); this._destroyAll(this.get("authors")); this.set("authDelDone", true); } }, // Private methods _generateSomeBooks(author) { const bookCounter = Faker.random.number(10); for (let j = 0; j < bookCounter; j++) { const library = this._selectRandomLibrary(); this.store.createRecord("book") .randomize(author, library) .save(); author.save(); library.save(); } }, _selectRandomLibrary() { const libraries = this.get("libraries"); const librariesCounter = libraries.get("length"); // Create a new array from IDs const libraryIds = libraries.map((lib) => {return lib.get("id");}); const randomNumber = Faker.random.number(librariesCounter-1); const randomLibrary = libraries.findBy("id", libraryIds[randomNumber]); return randomLibrary; }, _destroyAll(records) { records.forEach((item) => { item.destroyRecord(); }); } });
重啟項目,進入到http://localhost:4200/admin/seeder。在輸入框內(nèi)輸入要生成的測試數(shù)據(jù)條數(shù),然后點擊右邊的藍色按鈕,如果生成成功可以在按鈕右邊看到綠色的“created”提示文字。如下圖:
然后到firebase上查看??梢钥吹綌?shù)據(jù)已經(jīng)存在了,并且是隨機的數(shù)據(jù)。
并且是實現(xiàn)了數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系,比如一個author對應(yīng)多個book,如下圖。
或者是直接在http://localhost:4200/libraries下查看。
在接下來的一篇文章中將介紹如何遍歷關(guān)聯(lián)關(guān)系中的對象,使用起來也是非常簡單的,直接使用面向?qū)ο蟮姆绞奖闅v即可。
家庭作業(yè)本篇的家庭作業(yè)仍然是好好理解組件!參考下面的文章認真學習、理解組件。
Ember.js 入門指南之二十八組件定義
Ember.js 入門指南之二十九屬性傳遞
Ember.js 入門指南之三十包裹內(nèi)容
Ember.js 入門指南之三十一自定義包裹組件的HTML標簽
Ember.js 入門指南之三十二處理事件
Ember.js 入門指南之三十三action觸發(fā)變化
為了照顧懶人我把完整的代碼放在GitHub上,如有需要請參考參考。博文經(jīng)過多次修改,博文上的代碼與github代碼可能有出入,不過影響不大!如果你覺得博文對你有點用,請在github項目上給我點個star吧。您的肯定對我來說是最大的動力?。?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79552.html
摘要:本系列教材將為讀者介紹怎么樣使用構(gòu)建一個復雜的項目。本教程分為個小部分,通過這篇文章一步步為你講解怎么使用構(gòu)建一個稍微復雜的項目。說明本教程是基于而作,請注意與你自己的版本區(qū)別,如果出現(xiàn)不兼容問題請自行升級項目。 文章來源:http://xcoding.tech/tags/Ember-Demo/ 聲明:希望本系列教程能幫助更多學習Ember.js的初學者。 本系列教材將為讀者介紹怎么樣...
摘要:關(guān)鍵字對象關(guān)系映射現(xiàn)代的應(yīng)用程序常常是使用兩種截然不同的技術(shù)構(gòu)建而成業(yè)務(wù)邏輯部分使用面向?qū)ο缶幊蹋瑪?shù)據(jù)存儲使用關(guān)系型數(shù)據(jù)庫。對象關(guān)系映射則是兩者之間的橋梁,它允許應(yīng)用程序以面向?qū)ο蟮姆绞皆L問關(guān)系數(shù)據(jù)。 O/RM技術(shù)可以簡化數(shù)據(jù)訪問,但也需要注意到引入這個新的抽象層來的挑戰(zhàn)。 關(guān)鍵字:對象-關(guān)系映射 現(xiàn)代的應(yīng)用程序常常是使用兩種截然不同的技術(shù)構(gòu)建而成:業(yè)務(wù)邏輯部分使用面向?qū)ο缶幊?..
閱讀 2595·2023-04-25 20:50
閱讀 3953·2023-04-25 18:45
閱讀 2226·2021-11-17 17:00
閱讀 3332·2021-10-08 10:05
閱讀 3083·2019-08-30 15:55
閱讀 3498·2019-08-30 15:44
閱讀 2363·2019-08-29 13:51
閱讀 1121·2019-08-29 12:47