摘要:創(chuàng)建模型并設置關聯關聯關系設置模型關系一個對應多個,一個對應多個。手動在中增加關聯關系。并且是實現了數據表之間的關聯關系,比如一個對應多個,如下圖。
文章來源:模型高級特性,引入模型關聯關系
接著前面五篇:
環(huán)境搭建以及使用Ember.js創(chuàng)建第一個靜態(tài)頁面
引入計算屬性、action、動態(tài)內容
模型,保存數據到數據庫
發(fā)布項目,加入CRUD功能
從服務器獲取數據,引入組件
前言本篇主要是介紹模型直接的關聯關系,比如:一對一、一對多關系。會創(chuàng)建兩個模型author和book,設置它們的關系,并增加測試數據。
創(chuàng)建模型并設置關聯關聯關系設置API:
belongsTo
hasMany
模型關系:一個library對應多個book,一個author對應多個book。關系圖如下:
使用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關聯關系。
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)建。相關代碼如下:
// 其他代碼不變,省略 this.route("admin", function() { this.route("invitations"); this.route("contacts"); this.route("seeder"); }); // 其他代碼不變,省略
修改導航模板navbar.hbs增加新建路由的入口鏈接。
使用Ember.RSVP.hash()在一個路由中返回多個模型的數據Ember支持在一個路由的model回調中返回多個模型的數據。有關方法發(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()回調中返回了三個模型的數據:library、book和author。需要注意的是:上述代碼中方法Ember.RSVP.hash()會發(fā)送3個請求,并且只有三個請求都成功才會執(zhí)行成功。
在setupController()回調中,把三個模型分別設置到controller中。
每個路由內都內置了很多方法,比如前面介紹的model、setupController、renderTemplate,這些都是內置在路由類中的方法,那么這些方法調用次序又是如何的呢?請看下面的代碼:
// 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í)行次序與上述代碼方法的次序是一致的。有關API請看下面網址的介紹:
init()
beforeModel(transition)
model(params, transition)
activate()
setupController(controller, model)
renderTemplate(controller, model)
數量顯示功能創(chuàng)建一個組件用于顯示各個模型數據的總數。
ember g component number-box
組件創(chuàng)建完畢之后在組件類中增加css類,使用屬性classNames設置。
// 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中設置數據,model回調會自動從服務器獲取數據,obj.length意思是調用length()方法獲取數據長度,然后直接顯示到模板上,效果如下截圖,由于后面兩個模型還沒有數據所以顯示省略號。
構建表單生成測試數據前面已經介紹過屬性的傳遞,下面的代碼將為讀者介紹一些更加高級的東西!!一大波代碼即將來臨!!!
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"]的作用是根據屬性isShowing的值判斷是否添加CSS類label-show到標簽span上。更多有關信息請看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; } }
最主要、最關鍵的部分來了。
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用于關聯控制器中的action方法,屬性createReady和deleteReady是標記屬性。
等待項目重啟完畢,頁面結果如下:
底部的兩個輸入框用于獲取生成的數據條數。
安裝faker.js構建測試數據使用faker.js構建測試數據。
ember install ember-faker
安裝完畢之后擴展各個模型,并在模型中調用randomize()方法產生數據。下面是各個模型的代碼。
// 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設置為true的作用是:在獲取book的同時會把關聯的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。在輸入框內輸入要生成的測試數據條數,然后點擊右邊的藍色按鈕,如果生成成功可以在按鈕右邊看到綠色的“created”提示文字。如下圖:
然后到firebase上查看。可以看到數據已經存在了,并且是隨機的數據。
并且是實現了數據表之間的關聯關系,比如一個author對應多個book,如下圖。
或者是直接在http://localhost:4200/libraries下查看。
在接下來的一篇文章中將介紹如何遍歷關聯關系中的對象,使用起來也是非常簡單的,直接使用面向對象的方式遍歷即可。
家庭作業(yè)本篇的家庭作業(yè)仍然是好好理解組件!參考下面的文章認真學習、理解組件。
Ember.js 入門指南之二十八組件定義
Ember.js 入門指南之二十九屬性傳遞
Ember.js 入門指南之三十包裹內容
Ember.js 入門指南之三十一自定義包裹組件的HTML標簽
Ember.js 入門指南之三十二處理事件
Ember.js 入門指南之三十三action觸發(fā)變化
為了照顧懶人我把完整的代碼放在GitHub上,如有需要請參考參考。博文經過多次修改,博文上的代碼與github代碼可能有出入,不過影響不大!如果你覺得博文對你有點用,請在github項目上給我點個star吧。您的肯定對我來說是最大的動力!!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79552.html
摘要:本系列教材將為讀者介紹怎么樣使用構建一個復雜的項目。本教程分為個小部分,通過這篇文章一步步為你講解怎么使用構建一個稍微復雜的項目。說明本教程是基于而作,請注意與你自己的版本區(qū)別,如果出現不兼容問題請自行升級項目。 文章來源:http://xcoding.tech/tags/Ember-Demo/ 聲明:希望本系列教程能幫助更多學習Ember.js的初學者。 本系列教材將為讀者介紹怎么樣...
摘要:關鍵字對象關系映射現代的應用程序常常是使用兩種截然不同的技術構建而成業(yè)務邏輯部分使用面向對象編程,數據存儲使用關系型數據庫。對象關系映射則是兩者之間的橋梁,它允許應用程序以面向對象的方式訪問關系數據。 O/RM技術可以簡化數據訪問,但也需要注意到引入這個新的抽象層來的挑戰(zhàn)。 關鍵字:對象-關系映射 現代的應用程序常常是使用兩種截然不同的技術構建而成:業(yè)務邏輯部分使用面向對象編程...
閱讀 2584·2023-04-25 20:50
閱讀 3929·2023-04-25 18:45
閱讀 2214·2021-11-17 17:00
閱讀 3323·2021-10-08 10:05
閱讀 3073·2019-08-30 15:55
閱讀 3487·2019-08-30 15:44
閱讀 2355·2019-08-29 13:51
閱讀 1111·2019-08-29 12:47