摘要:鑒于此目的,我決定快速構建一個用于此目的的問卷調查應用程序。這將啟動一個服務器并將應用程序部署到該服務器。圖應用程序配置基礎前端這個問卷調查應用程序對常見用戶界面和布局使用了框架。
全棧教程。轉自 使用 Node.js、Express、AngularJS 和 MongoDB 構建一個實時問卷調查應用程序
最近,在向大學生們介紹 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)的一鍵式部署。
構建該應用程序的先決條件基本了解 Node.js 和 Node.js 開發環境
具有以下這些 Node.js 模塊:Express framework、Jade、Mongoose 和 socket.io
AngularJS JavaScript 框架
MongoDB NoSQL 數據庫
Eclipse IDE,已安裝了 Nodeclipse 插件
第 1 步. 構建一個基礎 Express 后臺在 Eclipse 中,切換到 Node 透視圖,并創建一個新的 Node Express 項目。如果您創建了一個 JazzHub 項目,請像我所做的那樣,使用相同的名稱為您的 Node Express 項目命名。選擇使用 Jade 作為模板引擎。Eclipse 會自動下載所需的 npm 模塊,以便創建一個簡單 Express 應用程序。
運行這個 Express 應用程序在 Project Explorer 中,找到位于您項目的根目錄中的 app.js,右鍵單擊并選擇 Run As > Node Application。這將啟動一個 Web 服務器并將應用程序部署到該服務器。
接下來,打開瀏覽器并導航到 http://localhost:3000。
這個問卷調查應用程序對常見用戶界面和布局使用了 Bootstrap 框架。現在,讓我們對 Express 應用程序做一些改動來反映這一點。首先,打開 routes/index.js,將標題屬性更改為 Polls:
exports.index = function(req, res){ res.render("index", { title: "Polls" }); };
接著,更改 views/index.jade 模板以包含 Bootstrap。Jade 是一種速記模板語言,可編譯成 HTML。它使用縮進消除了對結束標簽的需求,極大地縮小了模板的大小。您只需要使用 Jade 作為主頁面布局即可。在下一步中,還可以使用 Angular 局部模板向這個頁面添加功能。
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="http://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 文件:
注意:在使用 Jade 模板時,注意適當縮進您的代碼,否則您會遇到麻煩。另外,還要避免使用混合縮進樣式,如果您嘗試這樣做,Jade 將會報錯。
第 2 步. 使用 AngularJS 提供前端用戶體驗如果要使用 Angular,首先需要在您的 HTML 頁面中包含它,還需要在 HTML 頁面中添加一些指令。在 views/index.jade 模板中,對 html 元素進行如下更改:
html(lang="en", ng-app="polls")。
在該文件的標頭中添加以下腳本元素:
script(src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js") script(src="http://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 模塊Angular 中令人影響較為深刻的特性就是數據綁定,在后臺模型發生改變時,該功能會自動更新您的視圖。這極大地減少了需要編寫的 JavaScript 的數量,因為它對凌亂的 DOM 操作任務進行了抽象。
在默認情況下,Express 發布了靜態資源,比如 JavaScript 源文件、CSS 樣式表以及位于您項目的公共目錄中的圖像。在公共目錄中,創建一個名為 javascripts 的新的子目錄。在這個子目錄中,創建一個名為 app.js 的文件。該文件將包含用于應用程序的 Angular 模塊,而且還定義了用于用戶界面的路由和模板:
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 控制器定義了應用程序的范圍,為要綁定的視圖提供數據和方法。
// 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 和模板
為了呈現來自控制器的數據,Angular 使用了局部 HTML 模板,該模板允許您使用占位符和表達式來包含數據和執行操作,比如條件和迭代操作。在公共目錄中,創建一個名為 partials 的新的子目錄。我們將為我們的應用程序創建 3 個局部模板,第一個局部模板將會展示可用投票的列表,我們將使用 Angular 通過一個搜索字段輕松過濾該列表。
Poll List
- No polls in database. Would you like to create one?
第二個局部模板允許用戶查看投票。它使用 Angular 切換指令來確定用戶是否已投票,并根據這些判斷,顯示一個就此次問卷調查進行投票的表格,或者一個包含顯示問卷調查結果的圖表。
View Poll
Question
{{poll.question}}Please select one of the following options.
{{choice.text}}
{{choice.votes.length}} {{poll.totalVotes}} votes counted so far. You voted for {{poll.userChoice.text}} .
第三個也是最后一個局部模板定義了支持用戶創建新的問卷調查的表單。它要求用戶輸入一個問題和三個選項。提供一個按鈕,以便允許用戶添加額外的選項。稍后,我們將驗證用戶至少輸入了兩個選項 — 因為如果沒有幾個選項,就不能稱之為問卷調查。
Create New Poll
最后,為了顯示結果,我們需要向 style.css 添加一些 CSS 聲明。將該文件的內容替換為以下內容:
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 中存儲數據為了存儲數據,該應用程序使用了 MongoDB 驅動程序和 Mongoose npm 模塊。它們允許應用程序與 MongoDB 數據庫進行通信。要獲得這些模塊,請打該應用程序根目錄中的 package.json 文件,并在依賴關系部分中添加以下這些代碼行:。
"mongodb": ">= 1.3.19", "mongoose": ">= 3.8.0",
保存文件,在 Project Explorer 中右鍵單擊并選擇 Run As > npm install。這將安裝 npm 模塊和其他所有依賴關系。
創建一個 Mongoose 模型在您應用程序的名為 models 的根目錄中創建一個新的子目錄,并在這個子目錄中創建一個名為 Poll.js 的新文件。在這個文件中,我們定義了我們的 Mongoose 模型,該模型將用于查詢數據,并以結構化數據的形式將這些數據保存到 MongoDB 。
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 路由
接下來,在您應用程序的根目錄下的 app.js 文件中設置一些路由,以便創建一些 JSON 端點,這些端點可用于根據 Angular 客戶端代碼來查詢和更新 MongoDB。找到 app.get("/", routes.index) 行,并將下列代碼添加到這一行的后面:
:
app.get("/polls/polls", routes.list); app.get("/polls/:id", routes.poll); app.post("/polls", routes.create);
現在,您需要實現這些功能。將 routes/index.js 文件的內容替換為下列代碼:
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 服務將數據綁定到前端
此時,設置后臺,以便啟用查詢,并將問卷調查數據保存到數據庫,但我們需要在 Angular 中做一些更改,以便讓它知道如何與數據庫進行通信。使用 Angular 服務很容易完成這項任務,它將與服務器端進行通信的過程包裝到了簡單的函數調用中:
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 更改如下:。
function PollListCtrl($scope, Poll) { $scope.polls = Poll.query(); } ...
更新 PollItemCtrl 函數,以便根據問卷調查的 ID 來查詢某個問卷調查:
... function PollItemCtrl($scope, $routeParams, Poll) { $scope.poll = Poll.get({pollId: $routeParams.pollId}); $scope.vote = function() {}; } ...
類似地,更改 PollNewCtrl 函數,以便在提交表單時將新調查數據發送到服務器。
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"); } }; }運行應用程序
您已經離成功不遠了!此時,應用程序應該允許用戶查看和搜索問卷調查數據、創建新的問卷調查并查看單個問卷調查的投票選項。在運行該應用程序之前,請確保您已經本地運行 MongoDB。這通常和打開一個終端或命令提示符以及運行 mongod 命令一樣簡單。確保在您運行應用程序時終端窗口處于打開狀態:
在運行應用程序之后,在您的瀏覽器中導航到 http://localhost:3000 并創建一些問卷調查。如果您單擊一個問卷調查,您就能夠看到可用的選項,但是,您無法實際對該問卷調查進行投票,或者暫時看不到問卷調查結果。我們將在下一步和最后一步中對此進行介紹。
第 4 步. 使用 Socket.io 進行實時投票Web Sockets 允許服務器端直接與客戶端通信以及向客戶端發送消息。
剩下的惟一需要構建的特性就是投票功能。該應用程序允許用戶進行投票,在他們投票后,會在所有已連接的客戶端上實時更新投票結果。使用 socket.io 模塊很容易完成這項工作,現在就讓我們來實現它吧。
打開您應用程序的根目錄中的 package.json 文件,將下列代碼添加到依賴關系部分:
"socket.io": "~0.9.16"。
保存文件,在 Package Explorer 中右鍵單擊,然后選擇 Run As
> npm install 來安裝 npm 模塊。
接下來,打開應用程序根目錄中的 app.js 文件, 刪除位于文件末尾的 server.listen... 代碼塊,將其替換為:
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 文件的結尾處:
// 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 套接字。Web Sockets 的后端功能現已創建完畢。目前剩下要做的工作是綁定前端,以發送和監聽套接字事件。最佳方法是添加一個新的 Angular 服務。使用以下代碼替換 public/javascripts 文件夾中的 services.js 文件:
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 消息。將原始控制器替換為:
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"); } }; }查看最終產品
問卷調查應用程序現已創建完成。確保 mongod 仍在運行,并在 Eclipse 中再次運行 Node 應用程序。在瀏覽器中輸入 http://localhost:3000,導航到一個問卷調查并進行投票。隨后您就可以看到結果。要查看實時更新,請找到您的本地 IP 地址,并用該地址替換 localhost。在您的局域網中,使用不同的機器(甚至智能手機或平板電腦也可以)導航到這個地址。當您在另一個設備上進行投票時,結果會顯示在該設備上,而且會自動發布到您的主要計算機瀏覽器上:
您剛才創建的這個問卷調查應用程序是一個不錯的起點,但還有很大的改進空間。在計劃創建這類應用程序時,我喜歡使用一種敏捷方法來定義用戶案例,并將項目劃分為幾塊來實現。對于這個項目,我使用了 JazzHub,通過將項目的附屬代碼和源代碼一起保存在一個云托管的存儲庫中,JazzHub 使得開發變得非常簡單。
如果您對您的應用程序感到很滿意,下一步就是跟全世界的人分享它。在過去,即使部署一個非常簡單的應用程序,可能也會是一場噩夢,但值得慶幸的是,那些日子已經一去不復返了。使用 IBM 新興的兼容 Cloud Foundry 的 Bluemix 平臺,您只需幾分鐘就可以通過最少的配置將您的應用程序部署到云中,一點都不麻煩。
結束語這對于開發人員,現在是一個很好的時機。我們手頭有大量框架和工具,它們使得開發大量應用程序不僅更簡單、更快速,而且更加令人感到愉快。在本文中,您學習了如何使用被稱為 MEAN 體系結構(Mongo、Express、Angular 和Node)的技術構建一個應用程序。該堆棧可能只需要一天時間就可以完成任務,遠遠超過了 LAMP 體系結構(Linux、Apache、MySQL 和 PHP),在 Web 應用程序開發和部署方面,該體系結構也許同樣會超越 LAMP 體系結構。對我而言,我已經迫不及待躍躍欲試了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/18745.html
摘要:自年發布以來,走過了漫長的道路。一下子,工程師認為自己不只是前端開發者了。這種趨勢被稱為全棧的或純的解決方案。可以認為它是文檔結構的數據庫,而不是由行列表組成的數據庫。也是高度可測試的,這是很重要的。 JavaScript自1995年發布以來,走過了漫長的道路。已經有了幾個主要版本的ECMAScript規范,單頁Web應用程序也慢慢興起,還有支持客戶端的JavaScript框架。作為一...
摘要:一個標準性的事件就是年的橫空出世。引擎快速處理能力和異步編程風格,讓開發者從多線程中解脫了出來。其次,通過異步編程范式將其高并發的能力發揮的淋漓盡致。它也僅僅是一個處理請求并作出響應的函數,并無任何特殊之處。 showImg(https://segmentfault.com/img/remote/1460000010819116); 在正式學習 Express 內容之前,我們有必要從大...
摘要:年新星調查中顯示,越來越流行,其熱度已經逐漸超過了。及其框架位于全球最受歡迎使用最廣泛的技術榜榜首。本文轉載自框架的游戲年流行趨勢英文原文JavaScript 生態系統復雜多變,各種框架讓人眼花繚亂。究竟孰優孰劣,如今的發展趨勢是怎樣的,用人單位又需要怎樣的人才?本文站在一個中立者的角度,客觀分析了當前這場框架的游戲中,JavaScript 的流行趨勢。 Javascript 的生態環境讓我...
摘要:我所在的美團酒店事業部去年月份成立,新的業務新的開發團隊,這一切使得我們的前后端分離推進的很徹底。日志監控平臺日志監控平臺是美團內部的一個日志收集系統,目前美團統一使用收集日志,具有接收格式日志的能力,而日志監控平臺也是以格式日志來收集。 轉自:美團技術團隊 作者:美團技術團隊 分享理由:很好的分享,可見,基于Node的前后端分離的架構是越顯流行和重要,前端攻城獅們,No...
摘要:選擇是因為它簡單,適合高并發的服務,而且我們的開發人員能夠熟練使用它,關于的優缺點我在知乎上也曾經回答過使用的優勢和劣勢都有哪些。 其實早就該寫這篇博客了,一直說忙于工作沒有時間,其實時間擠擠總會有的,可能就是因為懶吧!從2013年11月一直拖到現在,其實我是不怎么擅長寫技術博客的,因為上學的時候語文不是很好,每次寫作文都不知道自己在寫啥,作為一開始就參與 Worktile 開發的技術...
閱讀 1123·2021-11-24 09:39
閱讀 3623·2021-09-02 15:21
閱讀 2161·2021-08-24 10:01
閱讀 722·2021-08-19 10:55
閱讀 2447·2019-08-30 15:55
閱讀 1212·2019-08-30 14:16
閱讀 2992·2019-08-29 15:17
閱讀 3235·2019-08-29 13:53