【全栈开发】精通 MEAN: 使用 MEAN 和 UGLI CRUD 实现响应式 Web 设计
您現(xiàn)在已經(jīng)了解了 MEAN 應(yīng)用程序的機(jī)制,接下來我們將對(duì)第一期文章中創(chuàng)建的 MEAN.JS應(yīng)用程序進(jìn)行定制。我們?cè)诘诙谖恼轮袑?duì)該應(yīng)用程序有了一個(gè)大致的了解。在第三期文章中,我將演示該應(yīng)用程序的基本 CRUD功能。您還會(huì)了解一些有關(guān)響應(yīng)式 Web 設(shè)計(jì)和 Bootstrap 的內(nèi)容。
本系列其余部分將要構(gòu)建的應(yīng)用程序被命名為 UGLI:User Group List and Information 應(yīng)用程序。我從 2010年開始運(yùn)營(yíng) HTML5 Denver User Group(前身是 Boulder Java User Group,更早以前是 Denver Java UserGroup),因此我是本地用戶組的狂熱粉絲,但是讓我不解的是一直沒有專門的軟件來運(yùn)行用戶組。現(xiàn)在我們就要解決這個(gè)問題了。
許多用戶組都在 Meetup.com 建立了一個(gè)在線主頁。我使用 MEAN 和UGLI 應(yīng)用程序的目標(biāo)并不是要取代 Meetup.com;相反,我想與它建立更深入的集成。Meetup.com集中了運(yùn)行成功的用戶組所需的大部分核心功能;注冊(cè)新用戶,發(fā)布會(huì)議細(xì)節(jié)、處理 RSVP等等。但是對(duì)于用戶組領(lǐng)導(dǎo)者來說仍然缺失一些關(guān)鍵功能,包括管理一組會(huì)議主持人(presenter)并鏈接到幻燈片(slide deck)。UGLI可以填補(bǔ)這方面的空缺。(參見 下載 獲得完整的樣例代碼)。
調(diào)整標(biāo)記
創(chuàng)建應(yīng)用程序 UGLI 的第一個(gè)任務(wù)就是調(diào)整應(yīng)用程序的標(biāo)記(branding)。需要在應(yīng)用程序的服務(wù)器端對(duì) config 和 app 目錄做一些修改;另外要對(duì)客戶端的 public 目錄做一些修改。
首先從 config/env/all.js 中的元數(shù)據(jù)開始。將標(biāo)題修改為 HTML5 Denver(或您選擇的用戶組),并將描述修改為 HTML5 Denver User Group,如清單 1 所示。
清單 1. config/env/all.js
'use strict';module.exports = {app: {title: 'HTML5 Denver',description: 'HTML5 Denver User Group',keywords: 'MongoDB, Express, AngularJS, Node.js'},config/env/development.js 中的標(biāo)題也需要修改,如清單 2 所示。上篇文章中我們已經(jīng)了解到 development.js 和 all.js 會(huì)在運(yùn)行時(shí)合并。
清單 2. config/env/development.js
'use strict';module.exports = {db: 'mongodb://localhost/test-dev',app: {title: 'HTML5 Denver'},接下來,修改導(dǎo)航欄左上角顯示的品牌。為此,需要編輯
public/modules/core/views/header.client.view.html。在大概第 9 列的地方找到 anchor 標(biāo)記和 navbar-brand 類,將 body 修改為 HTML5 Denver,如清單 3 所示。
清單 3. public/modules/core/views/header.client.view.html
<div class="container" data-ng-controller="HeaderController"><div class="navbar-header"><button class="navbar-toggle" type="button" data-ng-click="toggleCollapsibleMenu()"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a href="/#!/" class="navbar-brand">HTML5 Denver</a></div><!-- ...snip... --> </div>要驗(yàn)證所做的修改,請(qǐng)?jiān)诿钚休斎?mongod 啟動(dòng) MongoDB,然后輸入 grunt 啟動(dòng)應(yīng)用程序。在瀏覽器中查看 Web 應(yīng)用程序,看看標(biāo)記是否顯示在菜單和標(biāo)題欄中。
要完成標(biāo)記修改,需要替換 public/modules/core/views/home.client.view.html中的標(biāo)準(zhǔn)文本(boilerplate),該文本顯示在主頁的正文中。創(chuàng)建一個(gè)名為 home.client.view.html.original的副本,這樣就可以在稍后往回引用(如果需要的話)。
該文件利用 Bootstrap框架的功能,確保您的網(wǎng)站從一開始就面向移動(dòng)應(yīng)用。在繼續(xù)之前,需要了解 Bootstrap 提供的 12 列的網(wǎng)格布局。
了解 Bootstrap 和響應(yīng)式 Web 設(shè)計(jì)
查看任意硬拷貝新聞或雜志,您都會(huì)看到其中使用了列。有時(shí),一副圖片或標(biāo)題因?yàn)槟撤N設(shè)計(jì)風(fēng)格而需跨越多個(gè)列,但是一個(gè)基本的柱狀布局構(gòu)成了幾乎所有打印頁面的基礎(chǔ)。
Web 頁面也是如此。例如,訪問 TIME網(wǎng)站。您看到它的布局也是基于列的。但是,當(dāng)您將瀏覽器窗口的寬度從全屏縮小到非常窄的時(shí)候,注意會(huì)發(fā)生什么。可見列的數(shù)量將隨著窗口變小而減少,并隨著窗口增大而增多。
這種效應(yīng)被稱為 響應(yīng)式Web 設(shè)計(jì),因?yàn)?Web 頁面會(huì) 響應(yīng) 并調(diào)整設(shè)計(jì)來適應(yīng)設(shè)備所要求的屏幕尺寸。現(xiàn)代 Web開發(fā)人員構(gòu)建的網(wǎng)站可以無縫地支持從最小的手持設(shè)備到擁有最大屏幕的臺(tái)式機(jī)或壁掛屏幕等各種設(shè)備。分別使用 http://m.* 和http://www.* URL 為智能手機(jī)、平板電腦、筆記本等創(chuàng)建專門的、離散的網(wǎng)站,這種做法早已過時(shí)。
響應(yīng)式 Web 設(shè)計(jì) 并不是 一個(gè)全能的解決方案;相反,它是“一個(gè)外觀要求可以適應(yīng)所有設(shè)備的網(wǎng)站”。您不需要選擇用戶訪問網(wǎng)站所使用的設(shè)備類型,因此您的設(shè)計(jì)具備內(nèi)置的靈活性,可以相應(yīng)地進(jìn)行自我調(diào)整。
許多流行網(wǎng)站(包括 Facebook 和 Instagram)更多地是通過移動(dòng)設(shè)備而不是傳統(tǒng)計(jì)算機(jī)來進(jìn)行訪問的。Twitter的用戶群絕大多數(shù)是移動(dòng)用戶。Twitter 規(guī)范了其響應(yīng)式 Web 設(shè)計(jì)策略并實(shí)現(xiàn)了與 Bootstrap 相同的開源化。Bootstrap 有12 列的布局,可以根據(jù)您用來定義列的 CSS 類進(jìn)行縮小或放大。
請(qǐng)注意,MEAN.JS 應(yīng)用程序中對(duì) MongoDB、Express、AngularJS 和 Node.js 使用了四個(gè)列的布局,如圖 1 所示。
圖 1. Bootstrap 的列布局示例
現(xiàn)在查看 public/modules/core/views/home.client.view.html 中的源代碼,如清單 4 所示,看看Bootstrap 的 12 列布局是什么樣子的。
清單 4. public/modules/core/views/home.client.view.html
<div class="row"><div class="col-md-3"><h2><strong>M</strong>ongoDB</h2></div><div class="col-md-3"><h2><strong>E</strong>xpress</h2></div><div class="col-md-3"><h2><strong>A</strong>ngularJS</h2></div><div class="col-md-3"><h2><strong>N</strong>ode.js</h2></div> </div>如果您向一個(gè)父 div 添加 class="row",那么您可以向子div 添加 class="col-_xx_-_N_"屬性來將它們分成幾個(gè)列。_N_ 值必須介于 1 和 12之間,_xx_ 值取決于您希望優(yōu)化布局的設(shè)備的尺寸:
- xs 適用于極小設(shè)備(低于 768 像素寬)
- sm 用于小型設(shè)備(768 和 991 像素之間)
- md 適合中型設(shè)備(992 和 1,199 像素之間)
- lg 適合大型設(shè)備(1,200 像素或更高)
查看 Bootstrap CSS 文檔的 網(wǎng)格系統(tǒng)小節(jié),了解有關(guān)的更多信息。
由于清單 4 中的每個(gè)列針對(duì)中型(md)設(shè)備進(jìn)行了優(yōu)化,因此如果在屏幕寬度低于 992 像素的設(shè)備上訪問該頁面,列將垂直堆疊而不是水平堆疊。將您的瀏覽器窗口變得足夠窄來觸發(fā)這一更改,如圖 2 所示。
圖 2. 移動(dòng)設(shè)備上的響應(yīng)式 Web 設(shè)計(jì)示例
現(xiàn)在,可以使用我們已經(jīng)需到的知識(shí),使用特定于 UGLI 的文本替換 home.client.view.html 中的標(biāo)準(zhǔn)文本。
首先,從 W3C HTML5 徽標(biāo)頁面 下載256 像素的 HTML5 徽標(biāo),并將其復(fù)制到public/modules/core/img/brand/HTML5_Logo_256.png。然后使用清單 5 中的源代碼替換public/modules/core/views/home.client.view.html 中現(xiàn)有的 HTML。
清單 5. public/modules/core/views/home.client.view.html
<section data-ng-controller="HomeController"><div class="jumbotron text-center"><div class="row"><div class="col-md-4"><img alt="HTML5" class="img-responsive center-block" src="modules/core/img/brand/HTML5_Logo_256.png" /></div><div class="col-md-8"><h1>The HTML story is still being written.</h1> <h2><em>Come hear the latest chapter at the HTML5 Denver User Group.</em></h2></div></div></div> </section>在較寬的瀏覽器窗口中查看網(wǎng)站時(shí),HTML5 徽標(biāo)會(huì)出現(xiàn)在文本旁邊,如圖 3 所示。
圖 3. 新的 UGLI 主頁
當(dāng)您將瀏覽器窗口變得足夠窄時(shí),徽標(biāo)會(huì)出現(xiàn)在文本的上方,如圖 4 所示。
圖 4. 新的 UGLI 主頁,它會(huì)出現(xiàn)在移動(dòng)設(shè)備上
使用 Bootstrap 可以輕松地讓您的網(wǎng)站對(duì)移動(dòng)應(yīng)用程序變得更友好,我在為客戶構(gòu)建每個(gè)新網(wǎng)站時(shí)都使用 Bootstrap 作為基礎(chǔ)技術(shù)。
現(xiàn)在我們將要在 MEAN 堆棧中處理 CRUD。
基礎(chǔ) CRUD
Meetup.com 可以幫助我很好地管理用戶組活動(dòng)。但是,在某個(gè)活動(dòng)結(jié)束后,就時(shí)間方面而言,該活動(dòng)的重要性不如當(dāng)天晚上的談話。
換句話說,這個(gè)網(wǎng)站的一個(gè)用戶用例就是:“下次會(huì)議要討論什么?”Meetup.com 可以很好地滿足這種用戶用例。
第二個(gè)用戶用例(“向我顯示與 MEAN 堆棧有關(guān)的所有談話,不管是什么時(shí)候發(fā)生的” )正是我準(zhǔn)備通過 UGLI應(yīng)用程序解決的用例。要實(shí)現(xiàn)這個(gè)用例,必須圍繞一個(gè)新的名為 Talk 的模型對(duì)象創(chuàng)建一個(gè) CRUD
基礎(chǔ)架構(gòu)。幸運(yùn)的是,可以使用一個(gè) Yeoman 生成器來實(shí)現(xiàn)這個(gè)基礎(chǔ)架構(gòu)。
在應(yīng)用程序的根目錄,輸入 yo meanjs:crud-module talks。響應(yīng)提示:
清單 6 顯示了交互式命令行序列。
清單 6. 使用 Yeoman 生成器生成一個(gè)新的 CRUD
模塊
$ yo meanjs:crud-module talks [?] Which supplemental folders would you like to include in your angular module? css, img, directives, filters [?] Would you like to add the CRUD module links to a menu? Yes [?] What is your menu identifier? topbarcreate app/controllers/talks.server.controller.jscreate app/models/talk.server.model.jscreate app/routes/talks.server.routes.jscreate app/tests/talk.server.model.test.jscreate public/modules/talks/config/talks.client.routes.jscreate public/modules/talks/controllers/talks.client.controller.jscreate public/modules/talks/services/talks.client.service.jscreate public/modules/talks/tests/talks.client.controller.test.jscreate public/modules/talks/config/talks.client.config.jscreate public/modules/talks/views/create-talk.client.view.htmlcreate public/modules/talks/views/edit-talk.client.view.htmlcreate public/modules/talks/views/list-talks.client.view.htmlcreate public/modules/talks/views/view-talk.client.view.htmlcreate public/modules/talks/talks.client.module.js在清單 6 中,請(qǐng)注意,生成器創(chuàng)建了服務(wù)器端基礎(chǔ)架構(gòu)(保存在 app 目錄中):路由、一個(gè)控制器、一個(gè)模型和一個(gè)單元測(cè)試。它還在public/modules/talks 目錄下構(gòu)建了所有客戶端工件。
您稍后將向 Talk 對(duì)象添加一些自定義字段。在此之前,在瀏覽器中訪問網(wǎng)站,查看默認(rèn)情況下會(huì)得到哪些內(nèi)容。
單擊右上角的 Signin 鏈接,輸入本系列早些時(shí)候創(chuàng)建的用戶名和密碼,或者單擊Signup 并創(chuàng)建一組新的憑證。
完成登錄后,可以在左上角看到一個(gè) Talks 菜單。從菜單中選擇 New Talk打開一個(gè) HTML 表單,其中提供了一個(gè)獨(dú)立的 Name 字段,如圖 5 所示。
圖 5. 自定義之前的 New Talk 表單
這是一個(gè)良好的開端,但是要捕捉 Talk 的所有屬性,您需要的不僅僅是一個(gè)簡(jiǎn)單文本。
添加新字段實(shí)現(xiàn)持久性
要向 Talk 添加新字段,必須編輯 6 個(gè)文件 — 四個(gè)用于顯示,兩個(gè)用于持久性:
- app/models/talk.server.model.js
- public/modules/controllers/talks.client.controller.js
- public/modules/talks/views/create-talk.client.view.html
- public/modules/talks/views/edit-talk.client.view.html
- public/modules/talks/views/view-talk.client.view.html
- public/modules/talks/views/list-talks.client.view.html
首先要處理持久性。解決方案一半用在服務(wù)器端,另一半用在客戶端。
服務(wù)器端模型(在 app/models/talk.server.model.js中定義)是應(yīng)用程序的原型。您將在其中命名字段,提供數(shù)據(jù)類型,驗(yàn)證規(guī)則等等。
客戶端控制器(在 public/modules/controllers/talks.client.controller.js中定義)收集來自用戶的數(shù)據(jù)輸入,并通過 HTTP 請(qǐng)求將數(shù)據(jù)推到服務(wù)器。控制器還通過連接獲得 JSON 數(shù)據(jù),并提供給視圖以用于演示。
此架構(gòu)的一個(gè)有趣之處是對(duì)象模型永遠(yuǎn)不會(huì)離開服務(wù)器。對(duì)象是來自客戶機(jī)的數(shù)據(jù)的具體化實(shí)現(xiàn),并在 HTTP 響應(yīng)中序列化到 JSON。
該應(yīng)用程序有兩個(gè)控制器(一個(gè)位于服務(wù)器端,另一個(gè)位于客戶端),但是我們只關(guān)心客戶端控制器。服務(wù)器端控制器只是將進(jìn)入的 JSON推入到模型對(duì)象。因此在向模型添加額外字段時(shí)不需要對(duì)服務(wù)器端控制器做任何調(diào)整。客戶端控制器要進(jìn)行一些調(diào)整來容納新的字段。
打開 app/models/talk.server.model.js,向服務(wù)器端模型添加新的字段,如清單 7 所示。您可以看到展開的
name 字段(如 圖 5
所示),同時(shí)還定義了兩個(gè)元數(shù)據(jù)字段:created 和 user。
清單 7. app/models/talk.server.model.js
/*** Talk Schema*/ var TalkSchema = new Schema({name: {type: String,default: '',required: 'Please fill Talk name',trim: true},created: {type: Date,default: Date.now},user: {type: Schema.ObjectId,ref: 'User'} });這個(gè)基于 JSON 的模式無需多加解釋。在定義新字段時(shí),您可以指定數(shù)據(jù)類型、默認(rèn)值和錯(cuò)誤消息,以顯示給必要的字段。您還可以做出許多其他優(yōu)化。查看 Mongoosedocumentation,獲得有關(guān)的更多信息。
對(duì) description、presenter 和 slidesUrl添加新字段,如清單 8 所示。在本例中,description 和 presenter都是必要字段。slidesUrl 字段是可選字段。
清單 8. app/models/talk.server.model.js
/*** Talk Schema*/ var TalkSchema = new Schema({name: {type: String,default: '',required: 'Please fill Talk name',trim: true},description: {type: String,default: '',required: 'Please fill Talk description',trim: true}, presenter: {type: String,default: '',required: 'Please fill Talk presenter',trim: true},slidesUrl: {type: String,default: '',trim: true},created: {type: Date,default: Date.now},user: {type: Schema.ObjectId,ref: 'User'} });此時(shí),您的服務(wù)器端后端已經(jīng)準(zhǔn)備好接收新字段。現(xiàn)在您需要處理客戶端控制器。打開public/modules/controllers/talks.client.controller.js,添加新的字段,如清單 9 所示。
清單 9. public/modules/controllers/talks.client.controller.js
// Create new Talk $scope.create = function() {// Create new Talk objectvar talk = new Talks ({name: this.name,description: this.description,presenter: this.presenter,slidesUrl: this.slidesUrl});// Redirect after savetalk.$save(function(response) {$location.path('talks/' + response._id);}, function(errorResponse) {$scope.error = errorResponse.data.message;});// Clear form fieldsthis.name = '';this.description = '';this.presenter = '';this.slidesUrl = ''; };在 $scope.create 函數(shù)中,表格字段將被聚集到一個(gè) JSON對(duì)象,并被發(fā)送給服務(wù)器,以便實(shí)現(xiàn)持久存儲(chǔ)。從模型向控制器添加相應(yīng)的字段后,您就實(shí)現(xiàn)了持久存儲(chǔ)。
現(xiàn)在我們要將注意力轉(zhuǎn)移到演示層,這樣用戶就可以查看新字段并進(jìn)行交互。
添加新字段以進(jìn)行顯示
查看 public/modules/talks/views/。有四個(gè)字段與 CRUD 生命周期有關(guān):
- create-talk.client.view.html
- edit-talk.client.view.html
- view-talk.client.view.html
- list-talks.client.view.html
打開 create-talk.client.view.html,如清單 10 所示。
清單 10. 生成的
<section data-ng-controller="TalksController"><div class="page-header"><h1>New Talk</h1></div><div class="col-md-12"><form class="form-horizontal" data-ng-submit="create()" novalidate><fieldset><div class="form-group"><label class="control-label" for="name">Name</label><div class="controls"><input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required></div></div><div class="form-group"><input type="submit" class="btn btn-default"></div><div data-ng-show="error" class="text-danger"><strong data-ng-bind="error"></strong></div></fieldset></form></div> </section>將與 Name 有關(guān)的代碼塊復(fù)制三次,以便支持Description、Presenter 和slidesUrl,如清單 11 所示。我將 Description 字段設(shè)置為textarea,而不是一個(gè)簡(jiǎn)單的文本字段。同樣,我從 slidesUrl 字段移除了required 屬性,并將 input type 從 text修改為 url。
清單 11. 更新
create-talk.client.view.html
<section data-ng-controller="TalksController"><div class="page-header"><h1>New Talk</h1></div><div class="col-md-12"><form class="form-horizontal" data-ng-submit="create()" novalidate><fieldset><div class="form-group"><label class="control-label" for="name">Name</label><div class="controls"><input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required></div></div><div class="form-group"><label class="control-label" for="description">Description</label><div class="controls"><textarea data-ng-model="description" id="description" class="form-control" placeholder="Description" required></textarea></div></div><div class="form-group"><label class="control-label" for="presenter">Presenter</label><div class="controls"><input type="text" data-ng-model="presenter" id="presenter" class="form-control" placeholder="Presenter" required></div></div><div class="form-group"><label class="control-label" for="slidesUrl">Slides</label><div class="controls"><input type="url" data-ng-model="slidesUrl" id="slidesUrl" class="form-control" placeholder="Slides Url"></div></div> <div class="form-group"><input type="submit" class="btn btn-default"></div><div data-ng-show="error" class="text-danger"><strong data-ng-bind="error"></strong></div></fieldset></form></div> </section>在 Web 瀏覽器中,您新修改的 New Talk 頁面應(yīng)當(dāng)類似圖 6 所示。
圖 6. 自定義后的 New Talk 表單
如果對(duì)所做的更改感到滿意,請(qǐng)打開 edit-talk.client.view.html 并執(zhí)行相應(yīng)的更改,如清單 12 所示。
清單 12. edit-talk.client.view.html
<div class="col-md-12"><form class="form-horizontal" data-ng-submit="update()" novalidate><fieldset><div class="form-group"><label class="control-label" for="name">Name</label><div class="controls"><input type="text" data-ng-model="talk.name" id="name" class="form-control" placeholder="Name" required></div></div><div class="form-group"><label class="control-label" for="description">Description</label><div class="controls"><textarea data-ng-model="talk.description" id="description" class="form-control" placeholder="Description" required></textarea></div></div><div class="form-group"><label class="control-label" for="presenter">Presenter</label><div class="controls"><input type="text" data-ng-model="talk.presenter" id="name" class="form-control" placeholder="Presenter" required></div></div><div class="form-group"><label class="control-label" for="slidesUrl">Slides</label><div class="controls"><input type="url" data-ng-model="talk.slidesUrl" id="name" class="form-control" placeholder="Slides Url"></div></div><div class="form-group"><input type="submit" value="Update" class="btn btn-default"></div><div data-ng-show="error" class="text-danger"><strong data-ng-bind="error"></strong></div></fieldset></form> </div>請(qǐng)注意,用于編輯的 HTML 與之前修改的創(chuàng)建表單稍微有些不同。在編輯時(shí),您已經(jīng)有了一個(gè) Talk 對(duì)象,因此data-ng-model 屬性將以完全限定的方式引用字段,比如用 talk.name 而不是
name。在 Web 瀏覽器中查看修改,如圖 7 所示。
圖 7. 自定義后的 Edit Talk 表單
view-talk.client.view.html 頁面是對(duì)象的只讀視圖。用戶在保存新的 Talk,更新現(xiàn)有的Talk 或從列表頁面中選擇 Talk 后將來到該視圖。如清單 13 所示做出修改。
清單 13. edit-talk.client.view.html
<div class="page-header"><h1 data-ng-bind="talk.name"></h1><h2><em>by {{talk.presenter}} <span ng-if="talk.slidesUrl !== '' ">[<a href="{{talk.slidesUrl}}">slides</a>]</span></em></h2><p>{{talk.description}}</p> </div>前面提到 slidesUrl 是可選字段。在視圖頁面中,您將使用 ng-if指令有條件地顯示字段(如果已填充)。在瀏覽器中查看頁面,檢查這一行為,如圖 8 所示。
圖 8. 自定義后的 View Talk 表單
List 視圖是最后一個(gè)需要做出調(diào)整的視圖。打開 list-talks.client.view.html 并如清單 14 所示進(jìn)行修改。
清單 14. list-talks.client.view.html
<div class="list-group"><a data-ng-repeat="talk in talks" data-ng-href="#!/talks/{{talk._id}}" class="list-group-item"><h4 class="list-group-item-heading" data-ng-bind="talk.name"></h4><p><em>by {{talk.presenter}}</em></p></a> </div>請(qǐng)注意,這里使用 data-ng-repeat 指令顯示了服務(wù)器返回的 talk 列表中的每個(gè)talk。在瀏覽器中查看結(jié)果,如圖 9 所示。
圖 9. 自定義后的 List Talks 表單
結(jié)束語
此時(shí),您已經(jīng)了解了 MEAN 堆棧交互的各個(gè)方面。您使用 Bootstrap 的響應(yīng)式 Web 設(shè)計(jì)功能確保您的網(wǎng)站能夠適應(yīng)所有設(shè)備,而不僅限于傳統(tǒng)的有 101 個(gè)鍵和鼠標(biāo)的傳統(tǒng)臺(tái)式機(jī)。您已經(jīng)領(lǐng)略了使用 Yeoman 生成器向應(yīng)用程序添加新 CRUD模塊的強(qiáng)大之處及其便利性。該生成器將原始工件放到正確的目錄中,您只需要對(duì)它們進(jìn)行自定義即可。
下載
范例代碼:wa-mean3src.zip
原文出處:精通 MEAN: 使用 MEAN 和 UGLI CRUD 實(shí)現(xiàn)響應(yīng)式 Web 設(shè)計(jì)
總結(jié)
以上是生活随笔為你收集整理的【全栈开发】精通 MEAN: 使用 MEAN 和 UGLI CRUD 实现响应式 Web 设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 匿名者黑页html源码,匿名者 黑页源码
- 下一篇: 别不信!App三年内将被HTML5顶替彻