javascript
使用 Node.js、Express、AngularJS 和 MongoDB 构建一个Web程序
為什么80%的碼農都做不了架構師?>>> ??
使用 Node.js、Express、AngularJS 和 MongoDB 構建一個實時問卷調查應用程序
2014 年 3 月 20 日?04:53 ?|??抄本
Joe Lennon
高級移動應用程序開發人員
Joe Lennon 是來自愛爾蘭科克市的軟件開發人員,他今年 26 歲。Joe 是 Apress 即將發行的?Beginning CouchDB?一書的作者,為 IBM developerWorks 撰寫了許多技術文章和教程。在業余時間里,Joe 喜歡踢足球,改進一些小玩意和玩他的 Xbox 360 游戲機。
在 IBM Bluemix 云平臺上開發并部署您的下一個應用。
開始您的試用
最近,在向大學生們介紹 HTML5 的時候,我想要對他們進行問卷調查,并向他們顯示實時更新的投票結果。鑒于此目的,我決定快速構建一個用于此目的的問卷調查應用程序。我想要一個簡單的架構,不需要太多不同的語言和框架。因此,我決定對所有一切都使用 JavaScript — 對服務器端使用 Node.js 和 Express,對數據庫使用 MongoDB,對前端用戶界面使用 AngularJS。
這個 MEAN 堆棧(Mongo、Express、Angular 和 Node)只需要一天即可完成,遠比 Web 應用程序開發和部署所用的 LAMP 堆棧(Linux、Apache、MySQL 和 PHP)簡單得多。
運行該應用程序
在 JazzHub 上獲取源代碼
我選擇使用?JazzHub?來管理我的項目的源代碼。它不僅為我的代碼提供了一個完整的版本控制系統,還為在云中編輯代碼提供了一個在線 IDE,以及用于項目管理的敏捷特性。JazzHub 很容易與 Eclipse 相集成,Eclipse 還提供了一些插件,支持對平臺( 比如?Bluemix?或?Cloud Foundry)的一鍵式部署。
構建該應用程序的先決條件
0?
基本了解?Node.js?和 Node.js 開發環境
具有以下這些 Node.js 模塊:Express framework、Jade、Mongoose?和?socket.io
AngularJS?JavaScript 框架
MongoDB?NoSQL 數據庫
Eclipse IDE,已安裝了?Nodeclipse?插件
第 1 步. 構建一個基礎 Express 后臺
0?
在 Eclipse 中,切換到 Node 透視圖,并創建一個新的 Node Express 項目。如果您創建了一個 JazzHub 項目,請像我所做的那樣,使用相同的名稱為您的 Node Express 項目命名。選擇使用 Jade 作為模板引擎。Eclipse 會自動下載所需的 npm 模塊,以便創建一個簡單 Express 應用程序。
運行這個 Express 應用程序
0?
在 Project Explorer 中,找到位于您項目的根目錄中的 app.js,右鍵單擊并選擇?Run As?>?Node Application。這將啟動一個 Web 服務器并將應用程序部署到該服務器。 接下來,打開瀏覽器并導航到?http://localhost:3000。
圖 1. Starter Express 應用程序
配置基礎前端
0?
這個問卷調查應用程序對常見用戶界面和布局使用了?Bootstrap 框架。現在,讓我們對 Express 應用程序做一些改動來反映這一點。首先,打開 routes/index.js,將標題屬性更改為?Polls:
清單 1. routes/index.js
123?????????exports.index?=?function(req,?res){?????????????res.render('index',?{?title:?'Polls'?});?????????};接著,更改 views/index.jade 模板以包含 Bootstrap。Jade 是一種速記模板語言,可編譯成 HTML。它使用縮進消除了對結束標簽的需求,極大地縮小了模板的大小。您只需要使用 Jade 作為主頁面布局即可。在下一步中,還可以使用 Angular 局部模板向這個頁面添加功能。
清單 2. views/index.jade
12345678910111213141516?????????doctype?5????????html(lang='en')??????????head????????????meta(charset='utf-8')????????????meta(name='viewport',?content='width=device-width,??initial-scale=1,?user-scalable=no')????????????title=?title????????????link(rel='stylesheet',?href='//netdna.bootstrapcdn.com/bootstrap/3.0.1/?css/bootstrap.min.css')????????????link(rel='stylesheet',?href='/stylesheets/style.css')??????????????????????????body????????????nav.navbar.navbar-inverse.navbar-fixed-top(role='navigation')??????????????div.navbar-header????????????????a.navbar-brand(href='#/polls')=?title????????????div.container??????????????div想要查看對您的應用程序所做的更改,請結束 Eclipse 中的 Web 服務器進程,再次運行 app.js 文件:
圖 2. 問卷調查應用程序樣板文件
注意:在使用 Jade 模板時,注意適當縮進您的代碼,否則您會遇到麻煩。另外,還要避免使用混合縮進樣式,如果您嘗試這樣做,Jade 將會報錯。
第 2 步. 使用 AngularJS 提供前端用戶體驗
0?
如果要使用 Angular,首先需要在您的 HTML 頁面中包含它,還需要在 HTML 頁面中添加一些指令。在 views/index.jade 模板中,對?html?元素進行如下更改:
html(lang='en', ng-app='polls')。
在該文件的標頭中添加以下腳本元素: :?
清單 3. 將腳本元素加載到 Angular 和 Angular Resource 模板
123?????????script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js')????????script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular-resource.min.js')接下來,更改模板中的?body?元素,添加一個?ng-controller?屬性(稍后使用該屬性將用戶界面綁定到控制器邏輯代碼中):
body(ng-controller='PollListCtrl')。
最后,更改模板中的最后一個?div?元素,以便包含一個?ng-view?屬性:?div(ng-view)。
構建 Angular 模塊
0?
Angular 中令人影響較為深刻的特性就是數據綁定,在后臺模型發生改變時,該功能會自動更新您的視圖。這極大地減少了需要編寫的 JavaScript 的數量,因為它對凌亂的 DOM 操作任務進行了抽象。
在默認情況下,Express 發布了靜態資源,比如 JavaScript 源文件、CSS 樣式表以及位于您項目的公共目錄中的圖像。在公共目錄中,創建一個名為 javascripts 的新的子目錄。在這個子目錄中,創建一個名為 app.js 的文件。該文件將包含用于應用程序的 Angular 模塊,而且還定義了用于用戶界面的路由和模板:
清單 4. public/javascripts/app.js
1234567891011?????????angular.module('polls',?[])??????????.config(['$routeProvider',?function($routeProvider)?{????????????$routeProvider.??????????????when('/polls',?{?templateUrl:?'partials/list.html',?controller:?PollListCtrl?}).??????????????when('/poll/:pollId',?{?templateUrl:?'partials/item.html',?controller:?PollItemCtrl?}).??????????????when('/new',?{?templateUrl:?'partials/new.html',?controller:?PollNewCtrl?}).??????????????otherwise({?redirectTo:?'/polls'?});??????????}]);Angular 控制器定義了應用程序的范圍,為要綁定的視圖提供數據和方法。
清單 5. public/javascript/controllers.js
1234567891011121314151617181920??//?Managing?the?poll?list?function?PollListCtrl($scope)?{??????????$scope.polls?=?[];????????}?//?Voting?/?viewing?poll?results?function?PollItemCtrl($scope,?$routeParams)?{??????????$scope.poll?=?{};??????????$scope.vote?=?function()?{};????????}?//?Creating?a?new?poll?function?PollNewCtrl($scope)?{??????????$scope.poll?=?{????????????question:?'',????????????choices:?[{?text:?''?},?{?text:?''?},?{?text:?''?}]??????????};??????????$scope.addChoice?=?function()?{????????????$scope.poll.choices.push({?text:?''?});??????????};??????????$scope.createPoll?=?function()?{};????????}創建局部 HTML 和模板
0?
為了呈現來自控制器的數據,Angular 使用了局部 HTML 模板,該模板允許您使用占位符和表達式來包含數據和執行操作,比如條件和迭代操作。在公共目錄中,創建一個名為 partials 的新的子目錄。我們將為我們的應用程序創建 3 個局部模板,第一個局部模板將會展示可用投票的列表,我們將使用 Angular 通過一個搜索字段輕松過濾該列表。
清單 6. public/partials/list.html
123456789101112131415161718192021222324252627?????????<div?class="page-header">??????????<h1>Poll?List</h1>????????</div>????????<div?class="row">??????????<div?class="col-xs-5">????????????<a?href="#/new"?class="btn?btn-default"><span?class="glyphicon?glyphicon-plus"></span>?New?Poll</a>??????????</div>??????????<div?class="col-xs-7">????????????<input?type="text"?class="form-control"?ng-model="query"?placeholder="Search?for?a?poll">??????????</div>????????</div>????????<div?class="row"><div?class="col-xs-12"><hr></div></div>????????<div?class="row"?ng-switch?on="polls.length">??????????<ul?ng-switch-when="0">????????????<li><em>No?polls?in?database.?Would?you?like?to?<a?href="#/new">create?one</a>?</li>??????????</ul>??????????<ul?ng-switch-default>????????????<li?ng-repeat="poll?in?polls?|?filter:query">??????????????<a?href="#/poll/{{poll._id}}">{{poll.question}}</a>????????????</li>??????????</ul>????????</div>????????<p> </p>第二個局部模板允許用戶查看投票。它使用 Angular 切換指令來確定用戶是否已投票,并根據這些判斷,顯示一個就此次問卷調查進行投票的表格,或者一個包含顯示問卷調查結果的圖表。
清單 7. public/partials/item.html
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152?????????<div?class="page-header">??????????<h1>View?Poll</h1>????????</div>????????<div?class="well?well-lg">??????????<strong>Question</strong><br>{{poll.question}}????????</div>????????<div?ng-hide="poll.userVoted">??????????<p?class="lead">Please?select?one?of?the?following?options.</p>??????????<form?role="form"?ng-submit="vote()">????????????<div?ng-repeat="choice?in?poll.choices"?class="radio">??????????????<label>????????????????<input?type="radio"?name="choice"?ng-model="poll.userVote"??value="{{choice._id}}">????????????????{{choice.text}}??????????????</label>????????????</div>????????????<p><hr></p>????????????<div?class="row">??????????????<div?class="col-xs-6">????????????????<a?href="#/polls"?class="btn?btn-default"?role="button"><spanclass="glyphicon?glyphicon-arrow-left"></span>?Back?to?Poll??????????????</div>??????????????<div?class="col-xs-6">????????????????<button?class="btn?btn-primary?pull-right"?type="submit">?Vote?»</button>??????????????</div>????????????</div>??????????</form>????????</div>????????<div?ng-show="poll.userVoted">??????????<table?class="result-table">????????????<tbody>??????????????<tr?ng-repeat="choice?in?poll.choices">????????????????<td>{{choice.text}}</td>????????????????<td>??????????????????<table?style="width:?{{choice.votes.length?/poll.totalVotes*100}}%;">????????????????????<tr><td>{{choice.votes.length}}</td></tr>??????????????????</table>????????????????</td>??????????????</tr>????????????</tbody>??????????</table>????????????<p><em>{{poll.totalVotes}}?votes?counted?so?far.?<span?ng-show="poll.userChoice">You?voted?for?<strong>{{poll.userChoice.text}}</strong>.</span></em></p>??????????<p><hr></p>??????????<p><a?href="#/polls"?class="btn?btn-default"?role="button"><span?class="glyphicon?glyphicon-arrow-left"></span>?Back?to?Poll?List</a></p>????????</div>????????<p> </p>第三個也是最后一個局部模板定義了支持用戶創建新的問卷調查的表單。它要求用戶輸入一個問題和三個選項。提供一個按鈕,以便允許用戶添加額外的選項。稍后,我們將驗證用戶至少輸入了兩個選項 — 因為如果沒有幾個選項,就不能稱之為問卷調查。
清單 8. public/partials/new.html
12345678910111213141516171819202122232425262728293031323334353637?????????<div?class="page-header">??????????<h1>Create?New?Poll</h1>????????</div>????????<form?role="form"?ng-submit="createPoll()">??????????<div?class="form-group">????????????<label?for="pollQuestion">Question</label>????????????<input?type="text"?ng-model="poll.question"?class="form-control"?id="pollQuestion"?placeholder="Enter?poll?question">??????????</div>??????????<div?class="form-group">????????????<label>Choices</label>????????????<div?ng-repeat="choice?in?poll.choices">??????????????<input?type="text"?ng-model="choice.text"?class="form-control"?placeholder="Enter?choice?{{$index+1}}?text"><br>????????????</div>??????????</div>??????????????<div?class="row">????????????<div?class="col-xs-12">??????????????<button?type="button"?class="btn?btn-default"?ng-click="addChoice()"><span?class="glyphicon?glyphicon-plus"></span>?Add?another</button>????????????</div>??????????</div>??????????<p><hr></p>??????????<div?class="row">????????????<div?class="col-xs-6">??????????????<a?href="#/polls"?class="btn?btn-default"?role="button"><span?class="glyphicon?glyphicon-arrow-left"></span>?Back?to?Poll?List</a>????????????</div>????????????<div?class="col-xs-6">??????????????<button?class="btn?btn-primary?pull-right"?type="submit">?Create?Poll?»</button>????????????</div>??????????</div>??????????<p> </p>????????</form>最后,為了顯示結果,我們需要向 style.css 添加一些 CSS 聲明。將該文件的內容替換為以下內容:
清單 9. public/stylesheets/style.css
12345678910111213141516?????????body?{?padding-top:?50px;?}????????.result-table?{??????????margin:?20px?0;??????????width:?100%;??????????border-collapse:?collapse;????????}????????.result-table?td?{?padding:?8px;?}????????.result-table?>?tbody?>?tr?>?td:first-child?{??????????width:?25%;??????????max-width:?300px;??????????text-align:?right;????????}????????.result-table?td?table?{??????????background-color:?lightblue;??????????text-align:?right;????????}此時,如果您運行該應用程序,就會看到一個空的問卷調查列表。如果您試著創建一個新的問卷調查,就能看到此表單并添加更多的選項,但您不能保存該問卷調查。我們將在下一步中詳細介紹所有這些內容。
第 3 步. 使用 Mongoose 在 MongoDB 中存儲數據
0?
為了存儲數據,該應用程序使用了 MongoDB 驅動程序和 Mongoose npm 模塊。它們允許應用程序與 MongoDB 數據庫進行通信。要獲得這些模塊,請打該應用程序根目錄中的 package.json 文件,并在依賴關系部分中添加以下這些代碼行:。
清單 10. 向依賴關系添加下列代碼
12??"mongodb":?">=?1.3.19",?"mongoose":?">=?3.8.0",保存文件,在 Project Explorer 中右鍵單擊并選擇?Run As?>?npm install。這將安裝 npm 模塊和其他所有依賴關系。?
創建一個 Mongoose 模型
0?
在您應用程序的名為 models 的根目錄中創建一個新的子目錄,并在這個子目錄中創建一個名為 Poll.js 的新文件。在這個文件中,我們定義了我們的 Mongoose 模型,該模型將用于查詢數據,并以結構化數據的形式將這些數據保存到 MongoDB 。
清單 11. models/Poll.js?
12345678910??var?mongoose?=?require('mongoose');?var?voteSchema?=?new?mongoose.Schema({?ip:?'String'?});?var?choiceSchema?=?new?mongoose.Schema({???????????text:?String,??????????votes:?[voteSchema]????????});????????exports.PollSchema?=?new?mongoose.Schema({??????????question:?{?type:?String,?required:?true?},??????????choices:?[choiceSchema]????????});定義數據存儲的 API 路由
0?
接下來,在您應用程序的根目錄下的 app.js 文件中設置一些路由,以便創建一些 JSON 端點,這些端點可用于根據 Angular 客戶端代碼來查詢和更新 MongoDB。找到?app.get('/', routes.index)?行,并將下列代碼添加到這一行的后面: :
清單 12. 創建 JSON 端點
123?????????app.get('/polls/polls',?routes.list);????????app.get('/polls/:id',?routes.poll);????????app.post('/polls',?routes.create);現在,您需要實現這些功能。將 routes/index.js 文件的內容替換為下列代碼:
清單 13. routes/index.js
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455??var?mongoose?=?require('mongoose');?var?db?=?mongoose.createConnection('localhost',?'pollsapp');?var?PollSchema?=?require('../models/Poll.js').PollSchema;?var?Poll?=?db.model('polls',?PollSchema);????????exports.index?=?function(req,?res)?{??????????res.render('index',?{title:?'Polls'});????????};?//?JSON?API?for?list?of?polls????????exports.list?=?function(req,?res)?{???????????Poll.find({},?'question',?function(error,?polls)?{????????????res.json(polls);??????????});????????};?//?JSON?API?for?getting?a?single?poll????????exports.poll?=?function(req,?res)?{???var?pollId?=?req.params.id;??????????Poll.findById(pollId,?'',?{?lean:?true?},?function(err,?poll)?{?if(poll)?{?var?userVoted?=?false,??????????????????userChoice,??????????????????totalVotes?=?0;?for(c?in?poll.choices)?{?var?choice?=?poll.choices[c];??for(v?in?choice.votes)?{?var?vote?=?choice.votes[v];??????????????????totalVotes++;?if(vote.ip?===?(req.header('x-forwarded-for')?||?req.ip))?{????????????????????userVoted?=?true;????????????????????userChoice?=?{?_id:?choice._id,?text:?choice.text?};??????????????????}????????????????}??????????????}??????????????poll.userVoted?=?userVoted;??????????????poll.userChoice?=?userChoice;??????????????poll.totalVotes?=?totalVotes;??????????????res.json(poll);????????????}?else?{??????????????res.json({error:true});????????????}??????????});????????};?//?JSON?API?for?creating?a?new?poll????????exports.create?=?function(req,?res)?{?var?reqBody?=?req.body,??????????????choices?=?reqBody.choices.filter(function(v)?{?return?v.text?!=?'';?}),??????????????pollObj?=?{question:?reqBody.question,?choices:?choices};?var?poll?=?new?Poll(pollObj);??????????poll.save(function(err,?doc)?{?if(err?||?!doc)?{?throw?'Error';????????????}?else?{??????????????res.json(doc);????????????}?????????????});????????};使用 Angular 服務將數據綁定到前端
0?
此時,設置后臺,以便啟用查詢,并將問卷調查數據保存到數據庫,但我們需要在 Angular 中做一些更改,以便讓它知道如何與數據庫進行通信。使用 Angular 服務很容易完成這項任務,它將與服務器端進行通信的過程包裝到了簡單的函數調用中:
清單 14. public/javascripts/services.js
123456?????????angular.module('pollServices',?['ngResource']).??????????factory('Poll',?function($resource)?{?return?$resource('polls/:pollId',?{},?{??????????????query:?{?method:?'GET',?params:?{?pollId:?'polls'?},?isArray:?true?}????????????})??????????});在創建的這一文件之后,您需要將它包括在您的 index.jade 模板中。在文件標頭部分的最后一個腳本元素的下面添加以下這行代碼:
script(src='/javascripts/services.js')。
您還需要告訴您的 Angular 應用程序使用這個服務模塊。要實現這一點,請打開 public/javascripts/app.js 并將第一行更改為可讀,如下所示:
angular.module('polls', ['pollServices'])。
最后,更改 Angular 控制器,以便使用該服務在數據庫中進行查詢和存儲問卷調查數據。在 public/javascripts/controllers.js 文件中,將?PollListCtrl?更改如下:。
清單 15. public/javascripts/controller.js
1234??function?PollListCtrl($scope,?Poll)?{??????????$scope.polls?=?Poll.query();????????}???...更新?PollItemCtrl?函數,以便根據問卷調查的 ID 來查詢某個問卷調查:?
清單 16. public/javascripts/controller.js (continued)
123456?...?function?PollItemCtrl($scope,?$routeParams,?Poll)?{??????????$scope.poll?=?Poll.get({pollId:?$routeParams.pollId});??????????$scope.vote?=?function()?{};????????}?...類似地,更改?PollNewCtrl?函數,以便在提交表單時將新調查數據發送到服務器。
清單 17. public/javascripts/controller.js (continued)
123456789101112131415161718192021222324252627282930313233343536?...?function?PollNewCtrl($scope,?$location,?Poll)?{??????????$scope.poll?=?{????????????question:?'',????????????choices:?[?{?text:?''?},?{?text:?''?},?{?text:?''?}]??????????};????????????$scope.addChoice?=?function()?{????????????$scope.poll.choices.push({?text:?''?});??????????};??????????$scope.createPoll?=?function()?{?var?poll?=?$scope.poll;?if(poll.question.length?>?0)?{?var?choiceCount?=?0;?for(var?i?=?0,?ln?=?poll.choices.length;?i?<?ln;?i++)?{?var?choice?=?poll.choices[i];?????????if(choice.text.length?>?0)?{??????????????????choiceCount++????????????????}??????????????}?????if(choiceCount?>?1)?{?var?newPoll?=?new?Poll(poll);???????????????????????newPoll.$save(function(p,?resp)?{?if(!p.error)?{?????????????????????$location.path('polls');??????????????????}?else?{????????????????????alert('Could?not?create?poll');??????????????????}????????????????});??????????????}?else?{????????????????alert('You?must?enter?at?least?two?choices');??????????????}????????????}?else?{??????????????alert('You?must?enter?a?question');????????????}??????????};????????}運行應用程序
0?
您已經離成功不遠了!此時,應用程序應該允許用戶查看和搜索問卷調查數據、創建新的問卷調查并查看單個問卷調查的投票選項。在運行該應用程序之前,請確保您已經本地運行 MongoDB。這通常和打開一個終端或命令提示符以及運行?mongod?命令一樣簡單。確保在您運行應用程序時終端窗口處于打開狀態:
圖 3. 查看一個問卷調查的選項
在運行應用程序之后,在您的瀏覽器中導航到 http://localhost:3000 并創建一些問卷調查。如果您單擊一個問卷調查,您就能夠看到可用的選項,但是,您無法實際對該問卷調查進行投票,或者暫時看不到問卷調查結果。我們將在下一步和最后一步中對此進行介紹。
第 4 步. 使用 Socket.io 進行實時投票
0?
Web Sockets 允許服務器端直接與客戶端通信以及向客戶端發送消息。
剩下的惟一需要構建的特性就是投票功能。該應用程序允許用戶進行投票,在他們投票后,會在所有已連接的客戶端上實時更新投票結果。使用 socket.io 模塊很容易完成這項工作,現在就讓我們來實現它吧。
打開您應用程序的根目錄中的 package.json 文件,將下列代碼添加到依賴關系部分:
"socket.io": "~0.9.16"。
保存文件,在 Package Explorer 中右鍵單擊,然后選擇?Run As?>?npm install?來安裝 npm 模塊。
接下來,打開應用程序根目錄中的 app.js 文件, 刪除位于文件末尾的?server.listen...?代碼塊,將其替換為:
清單 18. app.js
123456789?...?var?server?=?http.createServer(app);?var?io?=?require('socket.io').listen(server);????????io.sockets.on('connection',?routes.vote);????????server.listen(app.get('port'),?function(){??????????console.log('Express?server?listening?on?port?'?+?app.get('port'));????????});接下來,修改 index.jade 模塊以包含 socket.io 客戶端庫。在運行該應用程序時,該庫會自動在指定的位置上變得可用,因此不需要擔心自己如何尋找該文件。確保想包含模板中的 angular-resource 庫的行的后面包含此文件:
script(src='/socket.io/socket.io.js')。
最后,您需要創建投票功能,以便在用戶向 socket.io 發送消息時保存新的投票,并在具有更新結果時將消息發送給所有客戶端。將 添加到路由目錄中的 index.js 文件的結尾處:
清單 19. routes/index.js
12345678910111213141516171819202122232425262728293031??//?Socket?API?for?saving?a?vote????????exports.vote?=?function(socket)?{??????????socket.on('send:vote',?function(data)?{?var?ip?=?socket.handshake.headers['x-forwarded-for']?||?socket.handshake.address.address;????????????????Poll.findById(data.poll_id,?function(err,?poll)?{?var?choice?=?poll.choices.id(data.choice);??????????????choice.votes.push({?ip:?ip?});????????????????????poll.save(function(err,?doc)?{?var?theDoc?=?{???????????????????question:?doc.question,?_id:?doc._id,?choices:?doc.choices,???????????????????userVoted:?false,?totalVotes:?0?????????????????};?for(var?i?=?0,?ln?=?doc.choices.length;?i?<?ln;?i++)?{?var?choice?=?doc.choices[i];??for(var?j?=?0,?jLn?=?choice.votes.length;?j?<?jLn;?j++)?{?var?vote?=?choice.votes[j];????????????????????theDoc.totalVotes++;????????????????????theDoc.ip?=?ip;?if(vote.ip?===?ip)?{??????????????????????theDoc.userVoted?=?true;??????????????????????theDoc.userChoice?=?{?_id:?choice._id,?text:?choice.text?};????????????????????}??????????????????}????????????????}???????????????????????socket.emit('myvote',?theDoc);????????????????socket.broadcast.emit('vote',?theDoc);??????????????});?????????????????});??????????});????????};注意:如果您想知道為什么應用程序會在常規 API 地址屬性的前面查找標頭?'x-forwarded-for',因為這將確保當應用程序被部署到負載平衡環境中時,所使用的是正確的客戶端 IP。如果您將該應用程序部署到 Bluemix 或 Cloud Foundry,這對于應用程序是否能正常工作至關重要。
添加一個 Angular 服務將數據發送到 Web 套接字。
0?
Web Sockets 的后端功能現已創建完畢。目前剩下要做的工作是綁定前端,以發送和監聽套接字事件。最佳方法是添加一個新的 Angular 服務。使用以下代碼替換 public/javascripts 文件夾中的 services.js 文件:
清單 20. public/javascripts/services.js
1234567891011121314151617181920212223242526272829?????????angular.module('pollServices',?['ngResource']).??????????factory('Poll',?function($resource)?{?return?$resource('polls/:pollId',?{},?{??????????????query:?{?method:?'GET',?params:?{?pollId:?'polls'?},?isArray:?true?}????????????})??????????}).??????????factory('socket',?function($rootScope)?{?var?socket?=?io.connect();?return?{??????????????on:?function?(eventName,?callback)?{????????????????socket.on(eventName,?function?()?{???var?args?=?arguments;??????????????????$rootScope.$apply(function?()?{????????????????????callback.apply(socket,?args);??????????????????});????????????????});??????????????},??????????????emit:?function?(eventName,?data,?callback)?{????????????????socket.emit(eventName,?data,?function?()?{?var?args?=?arguments;??????????????????$rootScope.$apply(function?()?{?if?(callback)?{??????????????????????callback.apply(socket,?args);????????????????????}??????????????????});????????????????})??????????????}????????????};??????????});最后,您需要編輯?PollItemCtrl?控制器,以便它能夠監聽和發送用于投票的 Web Socket 消息。將原始控制器替換為:
清單 21. public/javascripts/controllers.js
12345678910111213141516171819202122232425262728?...?function?PollItemCtrl($scope,?$routeParams,?socket,?Poll)?{???????????$scope.poll?=?Poll.get({pollId:?$routeParams.pollId});??????????socket.on('myvote',?function(data)?{????????????console.dir(data);?if(data._id?===?$routeParams.pollId)?{??????????????$scope.poll?=?data;????????????}??????????});??????????socket.on('vote',?function(data)?{????????????console.dir(data);?if(data._id?===?$routeParams.pollId)?{??????????????$scope.poll.choices?=?data.choices;??????????????$scope.poll.totalVotes?=?data.totalVotes;????????????}?????????????});??????????$scope.vote?=?function()?{?var?pollId?=?$scope.poll._id,????????????????choiceId?=?$scope.poll.userVote;?if(choiceId)?{?var?voteObj?=?{?poll_id:?pollId,?choice:?choiceId?};??????????????socket.emit('send:vote',?voteObj);????????????}?else?{??????????????alert('You?must?select?an?option?to?vote?for');????????????}??????????};????????}???...查看最終產品
0?
問卷調查應用程序現已創建完成。確保 mongod 仍在運行,并在 Eclipse 中再次運行 Node 應用程序。在瀏覽器中輸入 http://localhost:3000,導航到一個問卷調查并進行投票。隨后您就可以看到結果。要查看實時更新,請找到您的本地 IP 地址,并用該地址替換?localhost。在您的局域網中,使用不同的機器(甚至智能手機或平板電腦也可以)導航到這個地址。當您在另一個設備上進行投票時,結果會顯示在該設備上,而且會自動發布到您的主要計算機瀏覽器上:
圖 4. 查看問卷調查結果
下一步:進一步開發和部署
0?
您剛才創建的這個問卷調查應用程序是一個不錯的起點,但還有很大的改進空間。在計劃創建這類應用程序時,我喜歡使用一種敏捷方法來定義用戶案例,并將項目劃分為幾塊來實現。對于這個項目,我使用了 JazzHub,通過將項目的附屬代碼和源代碼一起保存在一個云托管的存儲庫中,JazzHub 使得開發變得非常簡單。
如果您對您的應用程序感到很滿意,下一步就是跟全世界的人分享它。在過去,即使部署一個非常簡單的應用程序,可能也會是一場噩夢,但值得慶幸的是,那些日子已經一去不復返了。使用 IBM 新興的兼容 Cloud Foundry 的?Bluemix?平臺,您只需幾分鐘就可以通過最少的配置將您的應用程序部署到云中,一點都不麻煩。
結束語
0?
這對于開發人員,現在是一個很好的時機。我們手頭有大量框架和工具,它們使得開發大量應用程序不僅更簡單、更快速,而且更加令人感到愉快。在本文中,您學習了如何使用被稱為 MEAN 體系結構(Mongo、Express、Angular 和Node)的技術構建一個應用程序。該堆??赡苤恍枰惶鞎r間就可以完成任務,遠遠超過了 LAMP 體系結構(Linux、Apache、MySQL 和 PHP),在 Web 應用程序開發和部署方面,該體系結構也許同樣會超越 LAMP 體系結構。對我而言,我已經迫不及待躍躍欲試了。
轉載于:https://my.oschina.net/iWage/blog/547794
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的使用 Node.js、Express、AngularJS 和 MongoDB 构建一个Web程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: poj2955Brackets(区间DP
- 下一篇: Linux内核的同步机制---自旋锁