0%

繼上一篇 (http://ithelp.ithome.com.tw/ironman7/app/article/dev/recent/10157760)上集介紹了 標籤、條件、迴圈、語法,本章節繼續介紹 Jade 的用法
分別是

  • comments
  • mixins
  • case
  • 使用 Jade 撰寫註冊表單

繼續使用 http://codepen.io/ 作為線上編輯做以下實作的測試編輯器

comments 註解

這是註解的寫法,註解的寫法就像 javascript 的方式一樣兩個斜線。若你想要把註解寫的方式不想要呈現在原始碼的話,那麼可以試著用 //- 的方式撰寫,這是滿好的手法之一。以下是單行註解跟多行註解的示範:

mixins

mixin 的用途是用在當某一段程式重複性高的時候,可以將它抽出來做 mixins,作為重複使用的區塊,mixin 可以將她當作 javascript 的 function 的概念

寫法

1
2
mixin 方法名稱(參數)
呈現資料

下圖的範例,我們將ul li 的 li 部分做 mixin,在第一行的地方,我們假定 li 裡面的內容要擺放 name = > price ,這就代表需要將參數放到方法的括弧裡面,因此,就如果有參數的話,就在方法名稱括號內,並以逗號分隔的方式擺參數,最後在 li 的地方就將參數值以 #{參數名} 帶入。在第五、第六行得地方呼叫的方式就用 + 號,後面接方法名稱,就像 js 呼叫的方式相同,寫為 +book(“name”, “price”)

case 條件式判斷

case 是一種類似 if else 的判斷方式,從下圖的語法來看,一開始建立一個變數為 book ,然後使用 case 後面再接變數,再第二行取到變數值之後,程式就會開始與 when 所設定的字串做比對,如果有符合,那麼就進行以下的陳述句,如果都沒有符合,那麼就會跑預設值 default,然而 default 可以不一定需要,這點可以省去

使用 Jade 撰寫註冊表單

從上一篇也是 jade 的學習到本篇的延續,大致上的語言都簡略的描述過一遍,接著我們需要來做個實際點的應用,我這邊使用的範例是經常網頁開發會使用到的註冊表單。簡單寫一些表單,那就用帳號、密碼、再加一個註冊的按鈕,如下圖所示:

Jade 課程就到這裡

參考資料

  1. http://jade-lang.com/
  2. http://naltatis.github.io/jade-syntax-docs/
  3. http://naltatis.github.io/jade-syntax-docs/#basics
  4. http://blog.kerash.tw/2013/04/nodejs-jade-template-engine/

Node.js 開發網頁它有許多的樣板引擎可以使用,例如常見的 Jade, EJS。樣板引擎也可以稱為樣板處理器 (Template Processor)或者叫過濾器 (Filter)。其他的語言也有個別使用的樣板引擎,像是 PHP 的 Smarty、 Ruby 的 ERB,本章節會以 jade 作為基礎的介紹如何透過樣板引擎實作 Node.js 網頁。

官方網站:http://jade-lang.com/

今天會介紹的內容有

  • Jade 特點
  • Jade 語法
    • 屬性
  • Jade 輸出資料
    • 迴圈
    • 條件

接下來您可以使用:http://codepen.io/ 作為線上編輯做以下實作的測試編輯器

首先,我們先來比較一下 HTML 跟 Jade 的表示方式

===== HTML =====

1
2
3
4
5
<div class="wrap">
<h1>Lorem Ipsum</h1>
<p>Contrary to popular belief, Lorem Ipsum is not simply random text. </p>
<a href=“http://www.google.com">Google</a>
</div>

===== Jade =====

1
2
3
4
.wrap
h1 Lorem Ipsum
p Contrary to popular belief, Lorem Ipsum is not simply random text.
a(href='http://www.google.com') Google

Jade 特點

  • Jade 用縮排來定義 HTML 的階層結構
  • Jade 比 HTML 寫起來更簡潔
  • Jade 在編譯後,會將 HTML 的補上結束標籤
  • Jade 不需要寫小於大於的符號

Jade 語法

因為 Jade 是利用縮排的方式來定義 HTML 的階層結構,也就是說,下一行的縮排的話,就是上一行的子節點。

撰寫

1
html

編譯後

1
<html></html>

撰寫

1
div#wrap

編譯後

1
<div id="wrap"></div>

撰寫

1
p.article.a1.a2.a3

編譯後

1
<p class="article a1 a2 a3"></p>

撰寫

1
2
p
span

編譯後

1
<p><span></span></p>

撰寫

1
2
3
4
p
| Contrary to popular belief
| Lorem Ipsum is simply dummy text of the printing
| It has survived not only five centuries

編譯後

1
<p>Contrary to popular belief Lorem Ipsum is simply dummy text of the printing It has survived not only five centuries </p>

標籤屬性:屬性部分就用括號的方式括弧起來,有多個屬性時就用逗點分隔或者段行來分隔

方法一

1
input(type="text", placeholder="hello")

方法二

1
2
3
4
5
input(
type='text'
name='age'
placeholder='typing your age'
)

方法三:也可以用 javascript 的判斷控制屬性資料

1
2
- var isFemale = true
input(type='text' value=isFemale ? 'Female' : 'Male')

Jade 輸出資料

迴圈

試著寫迴圈

輸出結果

條件

條件是指當判斷為真或否時,各別出現不同的結果,什麼意思呢?直接看範例好了

結果

Jade 就先寫到這裡,下集待續..

參考資料

  1. http://jade-lang.com/
  2. http://naltatis.github.io/jade-syntax-docs/
  3. http://naltatis.github.io/jade-syntax-docs/#basics
  4. http://blog.kerash.tw/2013/04/nodejs-jade-template-engine/

在管理套件工具的版本時,在 package.json 檔案裡面有一個 dependencies 的部分,裡面包含著套件的名稱與版本號,今天要談的是版本號的指定。

而這個版本號的指定是有個規範,它是語意化版本 http://semver.org/lang/zh-TW/ 根據它作為版號的規則

版本格式:主版號.次版號.修訂號 也就是 Major.Minor.Path 的形式

  1. 主版號:當你做了不相容的 API 修改,
  2. 次版號:當你做了向下相容的功能性新增,
  3. 修訂號:當你做了向下相容的問題修正。
    那麼在 package.json 檔案裡面的相依套件的版本要怎麼填呢?版本號的範圍?
  • 1.2.3, = 1.2.3 : 指定版本 1.2.3
  • 1.2.3 :大於 1.2.3

  • <1.2.3 :小於 1.2.3
  • =1.2.3 :大於 1.2.3

  • <=1.2.3:小於 1.2.3
  • 1.2.3 - 2.3.4 :大於 1.2.3 且小於 2.3.4
  • ~1.2.3 :合理接近 1.2.3 版本, 等價 >=1.2.3-0 <1.3.0-0
  • ~1.2 :等價 >1.2.0-0 <1.3.0-0,也就是 1.2 版本的同樣等價於 1.2.x 版本奧
  • ~1:等價於 >1.0.0-0 <2.0.0-0 ,也就是 1 開頭的版本等價於 1.x 版本號
    • :任意版本

最上面的圖,使用了好幾種定義版本的方式,就以 * 的寫法風險也比較大一點,因為無法保證 npm 安裝的版本是最新的版本,最保守一點的就是直接指定版本號,而如何得知版本號,可以透過

1
$npm info {套件名稱} version

方式去做查詢。


介紹 autod

https://github.com/node-modules/autod

這是一個自動分析所有的文件,引入的模組不需要手動更新 package.json ,並且它會同時自動更新 package.json

先看一下有哪些的 help 可以使用

接著開始執行 autod 的指令,在這邊我下了一個 -e 把 public, view 忽略,另外 -w 讓這份 package.json 能夠自動更新寫入

原始的 package.json

自動修正後的 package.json

是不是有很大的不同呢,而且又能夠自動更新,試試看囉

參考資料

  1. http://semver.org/lang/zh-TW/
  2. http://blog.eisneim.com/articles/2014-7-5-meaning-of-npm-bower-version-prefix.html
  3. https://cnodejs.org/topic/52dcc0a578990b04112e2f09

Heroku 是雲端運算的平台,您可以將您的應用程式放到雲端空間上面

註冊 Heroku 帳號

請先註冊一個 Heroku 帳號,https://www.heroku.com/,由於我們要部署上線的語法是 Node.js 語言,因此也確認您的 Node.js, npm 已安裝至您的電腦裡面。

安裝 Heroku Toolbelt 工具

這個工具將提供您 Command Line 的工具以及 git 版控工具,透過這個工具將輔助您接下來後續的超過指令,工具請至 https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up 下載,在這邊可以選擇您的作業系統環境。而我這邊將用 mac 做介紹

安裝首頁畫面,安裝過程基本上就是都下一步,偶爾出現可以自定的選項。

安裝進度

安裝後的檢查,請到命令字元輸入 heroku,如果出現以下訊息就代表安裝成功噢!

用指令登入 heroku , $heroku login ,按照指令輸入 email 與密碼

在這邊還需要建立一個 key ,請繼續輸入 $heroku keys ,如果沒有 key的存在,請手動建立 $heroku keys:add (https://devcenter.heroku.com/articles/keys),建立完成後會像以下結果:

部署程式到 Heroku,本章節選用來 http://ithelp.ithome.com.tw/ironman7/app/article/all/recent/10159261 專案進行初始化的建立,請先輸入

1
$heroku create

建立完成後可以看到訊息提到,配了一組 aqueous-dawn-4266 的名字,馬上轉到 heroku 上的 dashboard ,立即就看到上面多了一個名為 aqueous-dawn-4266 專案,但這時候程式還並沒有部署到 heroku ,目前已經建立遠端的檔案庫在上面。

您也許覺得 aqueous-dawn-4266 好難記,在 heroku 也提供了更名的指令,請輸入 $heroku apps:rename newname
我將專案名稱改成 nodejs-express-bower ,而 heroku 的 dashboard 專案列表也會跟著改噢

接下來就部署程式,請輸入

1
$git push heroku master

這時候進到 heroku dashboard ,選擇 nodejs-express-bower 專案,點選上方標題右邊的 icon

觀察結果顯示如下:

參考資料

https://devcenter.heroku.com/articles/getting-started-with-nodejs#introduction

上篇介紹了註冊,相對的就一定要來介紹登入的功能。在資料庫上定義了 username, userpass 的欄位,登入的時候就在頁面上擺上兩個輸入框,以及登入的按鈕。

在上一篇已經有建立好 models 的部分,在登入的操作也需要透過 models 向資料庫做存取,請打開 /models/user.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//檢查是否為會員
User.getUserNumByName = function getUserNumByName(username, callback) {
//使用username 來檢查是否有資料
var cmd = “select COUNT(1) AS num from user info where username = ?";
connection.query(cmd, [username], function (err, result) {
if (err) {
return;
}
connection.release();
//查詢結果使用 callback 呼叫,並將 err, result 參數帶入
callback(err,result);
});
};
//透過帳號取得使用者資料
User.getUserByUserName = function getUserNumByName(username, callback) {
var cmd = “select * from userinfo where username = ?";
connection.query(cmd, [username], function (err, result) {
if (err) {
return;
}
connection.release();
callback(err,result);
});
};

然後請在 routes/ 資料夾 (如果沒有請自行建立)新增 login.js 檔案,在這檔案要特別注意的是,當登入表單送出的時候,如果是會員的話,那麼在登入進到主頁時,將使用者帳號寫到 session 上面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var express = require('express'),
User = require('../models/user.js'),
router = express.Router(),
crypto = require('crypto'),
title = '登入';

router.get('/', function(req, res) {
res.render('login', {title:title});
});

router.post('/', function(req, res) {
var userName = req.body['txtUserName'],
userPwd = req.body['txtUserPwd'],
md5 = crypto.createHash('md5');

User.getUserByUserName(userName, function (err, results) {
if(results == '') {
res.locals.error = '使用者不存在';
res.render('login',{title:title});
return;
}

userPwd = md5.update(userPwd).digest('hex');
if(results[0].UserName != userName || results[0].UserPass != userPwd) {
res.locals.error = '使用者帳號或密碼錯誤';
res.render('login',{title:title});
return;
} else {
res.locals.username = userName;
//設定session
req.session.username = res.locals.username;
console.log(req.session.username);
res.redirect('/');
return;
}
});
});

module.exports = router;

一切基本動作準備好之後,接著就打開 app.js 新增 login 的 router 路由,而這個路由將真的表單的請求作處理,在以下的兩行程式所代表的是指定載入路由的寫法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var login = require('./routes/login’);
app.use('/login', login);

view 檢視 請在 /views/ 建立 index.ejs

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/bootstrap.min.css" />
</head>
<body>
<div class="container">
<% if (locals.username) { %>
<h1>您好, <%= username %></h1>
<a href="/logout">登出</a>
<% } %>
</div>
</body>
</html>

已知註冊的帳號密碼是 test / 123456

登出

有了登入之後,也別忘了登出,登出的寫法也相當容易,由於本章節是將註冊的帳號用 session 保存,而登出只需要將 session 銷毀即可,銷毀之後,再退到登入頁面。程式如下:

1
2
3
4
5
6
7
8
9
var express = require('express'),
router = express.Router();

router.get('/', function(req, res) {
req.session.destroy();
res.redirect('/login');
});

module.exports = router;

本篇將介紹 node.js 系列學習日誌以來的一些整合應用,預計來做註冊、登入、登出這類的動態程式,在這裏首先介紹註冊的部分,本著 node.js express web framework 的 mvc 架構,實際的應用,我們會預想著註冊時會用到哪些、資料庫要有哪些規劃安排。

請建立好 express web framework 框架,指令輸入

1
$express -e nodejs-auth

建立完成之後,打開 app.js 在 var app = express(); 新增

1
app.use('/reg', reg);

因為我們要使用 mysql 作為儲存的資料庫,因此建立一個 models/user.js ,其宣告資料庫如下

1
2
3
4
5
6
7
8
var mysql = require('mysql');
var DB_NAME = 'demo_nodejs';

var pool = mysql.createPool({
host : 'localhost',
user : 'nodejs',
password : 'nodejs'
});

定義資料庫欄位 id, UserName, UserPass ,其中 id 使自動增加號碼

因為註冊的時候只需要填寫帳號、密碼,因此在 models/user.js 的地方先建立一個 User 物件,定義兩個 username, userpass 屬性,如下

1
2
3
4
function User(user){
this.username = user.username;
this.userpass = user.userpass;
};

建立好 user 的 models 之後,開始要開發 routes 的部分,請先建立 /routes/reg.js 檔案,將以下程式碼填入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var express = require('express'),
router = express.Router(),
User = require('../models/user.js'),
crypto = require('crypto'),
TITLE_REG = '註冊';

router.get('/', function(req, res) {
res.render('reg',{title:TITLE_REG});
});

router.post('/', function(req, res) {
var userName = req.body['txtUserName'],
userPwd = req.body['txtUserPwd'],
userRePwd = req.body['txtUserRePwd'],
md5 = crypto.createHash('md5');

userPwd = md5.update(userPwd).digest('hex');

var newUser = new User({
username: userName,
userpass: userPwd
});
module.exports = router;

寫入資料庫

我們定義好 User 這個物件之後,接下來就是寫入的功能我們要善用學習日誌的一篇 (連接 MySQL 並實現 CRUD 操作 - 建立 (Create))文章,請詳見 http://ithelp.ithome.com.tw/ironman7/app/article/all/recent/10160394 我們再打開 models/users.js 這支檔案,將表單傳送的 insert 的動作方法,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
User.prototype.save = function save(callback) {
var user = {
username: this.username,
userpass: this.userpass
};

var insertUser_Sql = "INSERT INTO userinfo(id, username, userpass) VALUES(0,?,?)";

connection.query(insertUser_Sql, [user.username, user.userpass], function (err,result) {
if (err) {
return;
}

connection.release();
callback(err,result);
});
};

在章節的最前面的 UI 佈局

1
2
3
4
5
6
7
8
9
10
<form class="form-signin" role="form" method="post">
<h2 class="form-signin-heading">註冊</h2>

<input id="txtUserName" name="txtUserName" type="text" class="form-control" placeholder="帳號" required />
<input id="txtUserPwd" name="txtUserPwd" type="password" class="form-control" placeholder="密碼" required/>
<input id="txtUserRePwd" name="txtUserRePwd" type="password" class="form-control" placeholder="再輸入密碼" required/>

<button id="btnSub" class="btn btn-lg btn-primary" type="submit">註冊</button>
<a class="btn btn-link" href="/login" role="button">登入</a>
</form>

下一篇將介紹註冊完成後的登入操作。

參考資料

http://stackoverflow.com/questions/22994589/nodejs-express-routes-and-mysql

開發過程所撰寫的程式,總是要跟 bugs 打交道,本篇文章介紹一套官方推薦的 Node.js Debug 調校工具 node-inspector,而Node.js 是可以透過 command 命令列方式除錯,不過 node-inspector 則是透過 Web 網頁的方式來操作,讓開發者在調校更美觀方便一些。在這 node-inspector 安裝之前,我們必須要有一個 webkit 的瀏覽器,比如許多人使用的 chrome。接著我們就可以進行安裝 node-inspector

安裝指令

1
$npm install -g node-inspector

調校

執行時會預設 5858 port 口。我們使用前一個聊天室專案來示範 ,使進行 debug 動作時請下指令:

1
node —debug app.js

這時候會開始做 5858 port 的監聽,接著我們在打開另一個命令提示字元的視窗,在執行

1
$node-inspector &

上面會提示說請造訪 http://127.0.0.1:8080/debug?port=5858 路徑去啟動 debugging 模式

打開之後,可以看到調校的工具載入程式碼,您可以在行號下中斷點,右手邊的 breakpoints 就會顯示下中斷點的位置

在這邊我下了一個 建立新的使用者的中斷點,也就是新的使用者進到聊天室裡面,可以看到調校的頁面就立刻監測到,如下所示:

您可以將滑鼠指向 socket 的位置,可以看到 socket 相關的屬性及方法

以上的基本操作就可以很方便的調校 node.js 的程式,也不用一直在程式上面插旗子 (console.log 的方法)可以透過這個工具可以進行 debugging 囉

寫過網頁的大家們,大致上都了解 http 是一種無狀態的形式,假設頁面登進來的使用者,為了保存它的登入資訊,通常會使用 session 的觀念作為存放的物件,否則 server 端就不知道 client 端登進來的人是誰,不過今天不講session 的部分,而是我們要透過 socket.io 做雙向溝通,只要一旦建立了連線之後,就可以立即傳訊息出去,Client 的部分也立即接收到,相互取得訊息。

今天要來透過介紹的是簡單的聊天室功能,讓大家了解 node.js 在 socket.io 運作的方式,以下是本章節會學習到的部分:

  • 使用 Bootstrap 規劃製作前端頁面框架
  • 使用 express 套功能
  • 使用 socket.io 套件
  • 開發聊天室功能

本章節程式同步放置於 :https://github.com/weijutu/nodejs-simple-chatroom

首先我們先定義好需要的動向有:

  1. 進入聊天室之前需要先輸入姓名
  2. 進入聊天室頁面之後,左邊區域負責聊天的顯示部分,右邊區域負責登入的名單
  3. 聊天功能會有個按鈕,當使用者輸入訊息後,按下按鈕送出
  4. 瀏覽器關閉時,也等於退出聊天功能

首先建立一個 nodejs-simple-chatroom 資料夾,安裝 express, socket.io 相依套件,package.json 檔案內容為:

1
2
3
4
5
6
7
8
9
{
"name": "nodejs-simple-chatroom",
"version": "0.0.1",
"private": true,
"dependencies": {
"socket.io": "1.0.3",
"express": "4.4.0"
}
}

(安裝相依套件方式請詳情:http://ithelp.ithome.com.tw/ironman7/app/article/dev/recent/10158140)
建立 package.json 完成之後,先將畫面基礎規劃完成

client 端

當多開幾個瀏覽器,可以輸入進入聊天的姓名,以下是在聊天之前先輸入的欄位:

在這邊使用了 bootstrap 前端框架來美化一下聊天室外觀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<div class="container">
<div id="nickWrap">
<form id="setNick" role="form">
<h2 class="form-signin-heading">請輸入您的名字:</h2>
<p id="nickError"></p>
<div style="margin:20px;">
<input size="35" id="txtNickname" required="" />
</div>
<input type="Submit" class="btn btn-lg btn-primary">
</form>
</div>


<div id="contentWrap">
<div id="chatWrap" class="panel panel-default" >
<div class="panel-heading">即時聊天</div>
<div class="panel-body">
<div class="row">
<div class="col-md-9">
<div id="chat"></div>
<form id="send-message">
<div class="input-group">
<input type="text" class="form-control" placeholder="請輸入聊天訊息..." id="message">
<span class="input-group-btn">
<button type="submit" class="btn btn-default">送出留言</button>
</span>
</div>
</form>
</div>
<div class="col-md-3">
線上名單:
<div id="users"></div>
</div>
</div>
</div>
</div>

</div>

<script type="text/javascript" src="//code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>

</div>

javascript 部分

這部分寫在 client 裏面,作為控制像的事件觸發操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<script type="text/javascript">
$(function(){
var socket = io.connect();
var $frmMessage = $('#send-message');
var $frmNick = $('#setNick');
var $nickError = $('#nickError');
var $nickBox = $('#txtNickname');
var $boxMessage = $('#message');
var $chat = $('#chat');


$frmNick.submit(function(e){
console.log($nickBox.val());
console.log('hi, frmNick');
e.preventDefault();

socket.emit('new user', $nickBox.val() );

$nickBox.val('');

$('#nickWrap').hide();
$('#contentWrap').show();

});

$frmMessage.submit(function(e){
e.preventDefault();
socket.emit('send message', $boxMessage.val().trim());
$boxMessage.val('');
});

socket.on('usernames', function(data){
var sb = '';
for(var d = 0; d < data.length; d++ ) {
console.log(data[d]);
sb += data[d] + "<br />";
}
$('div#users').html(sb);

});

socket.on('chat', function(server,msg){

var now = new Date();
var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

$chat.append("<br /><i>系統訊息: <b>[ " + msg + " ]</b> (" +
datetime + ")</i><br />");
});

socket.on('new message', function(data){
var msg = data.msg;
var name = data.nick;

var now = new Date();
var datetime = now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate();
datetime += ' '+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds();

$chat.append("<b>" + name + " </b>: " + msg + " (<i>" + datetime + "<i>)<br />");
});
});
</script>

server 端:(app.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
nicknames = [];

server.listen(3000);

app.get('/', function(req, res){
res.sendfile(__dirname + '/index.html');
});
app.use('/public', express.static(__dirname + '/public'));

io.sockets.on('connection', function(socket) {
socket.on('new user', function(data){
console.log(data);
if (nicknames.indexOf(data) != -1) {

} else {
socket.emit('chat', 'SERVER', '歡迎光臨 ' + data);

socket.nickname = data;
nicknames.push(socket.nickname);
io.sockets.emit('usernames', nicknames);
updateNicknames();
}
});

function updateNicknames(){
io.sockets.emit('usernames', nicknames);
}

//
socket.on('send message', function(data){
io.sockets.emit('new message', { msg: data, nick: socket.nickname });
});

socket.on('disconnect', function(data){
if (!socket.nickname) return;
io.sockets.emit('chat', 'SERVER', socket.nickname + ' 離開了聊天室~');
nicknames.splice(nicknames.indexOf(socket.nickname), 1);
updateNicknames();
});
});

結果

已上圖來講,原本有三個聊天者,當某一個使用者關閉瀏覽器 離開聊天室時,將會把使用者從名單上移除。
本章節是個簡易的聊天室功能,當然也可以進階一點做一些顯示上的呈現,或者將這些的留言都使用資料庫(例如:mongodb)儲存這些訊息。

參考資料

http://getbootstrap.com/
http://expressjs.com
http://socket.io/
https://www.ptt.cc/bbs/joke/M.1412577767.A.0EB.html
http://segmentfault.com/a/1190000000479518

Node.js 有許多網頁的框架,其中以 Express 的框架最為流行,也是滿多人才討論使用。在本篇將介紹這個工具具有快速建立網站框架的方法。

我們用全域模式安裝 express ( $npm install -g express )之後就看一下 help 怎麼寫。

在下圖的地方,可以大概可以看到 express 的預設樣板引擎是 jade,若需要安裝為 ejs 的樣板時,那麼建立 express 的命令下

1
$express -e demo-express2

在這邊將自動的安裝相依套件 express, ejs ,接著我們打開 sublime 編輯器,看到 package.json ,你可以看到 dependencies 的物件裡面自動安裝了許多相依項目。

我們透過 express 指令建立好網站之後呢,可以看到他的提示指示

1
cd demo-express2 && npm install

將相依套件安裝起來,此時才會自動有 node_module 資料夾放置套件的位置,依照指令安裝好之後,我們就執行一下網站,請輸入:

1
$npm start

結果

參考資料

http://expressjs.com/
http://expressjs.com/4x/api.html#application
http://nodeframework.com/index.html

今天來介紹 express, ejs 實作讀取靜態 JSON 格式資料程式

本章節將會學習到:

  • 使用 ejs 樣板引擎建立檢視頁面
  • 建立 Json 檔案及資料,並採取 /api/posts 列出所有 json 資料
  • 採取 /post/:id 取得某一筆資料
    首先我們先建立一個目錄叫 demo-express-json 的資料夾,然後使用指令進入到 demo-express-json 資料夾裡面,接著安裝 express 框架以及 ejs 的樣板引擎
1
$npm install —save express ejs

建立 josn 檔案

建立完成之後,我們在 demo-express-json 會出現 node_module 資料夾,裡面則是有 express, ejs 套件相關檔案,接下來在根目錄下建立一個 posts.json 格式檔案,資料先假定設定為:

(文字是用亂數假文產生器弄的,因此無需認真讀囉)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
{
"title": "會濟報好大必者政下二",
"id": "fdb61c16-4bba-11e4-9e35-164230d1df67",
"content": "容有那一氣持地來於結主了友如頭……院還地入。出乎機富事的著度同禮、時在科種力事再數總源式孩?"
},
{
"title": "主覺問我的食什期和",
"id": "0bc365ac-4bbb-11e4-9e35-164230d1df67",
"content": "生老了險實去供考權是車子氣長不別相且員高麼也工家看"
},
{
"title": "是實裡在園時傳",
"id": "0ff44786-4bbb-11e4-9e35-164230d1df67",
"content": "不半過何:為濟是在制們。裡得一方出,處師取是你賣而陽"
}
]

這個 json 檔每一筆資料以 title, id, content 參數為主

建立 app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// express web framework
var express = require('express');
//讀取 posts.json
var fs = require('fs');
var app = express();

//放置靜態網頁
app.use('/public', express.static(__dirname + '/public'));

//定義網站標題
app.locals.title="Get json data using express web framework";

app.all('*', function(req, res, next){
fs.readFile('posts.json', function(err, data){
res.locals.posts = JSON.parse(data);
next();
});
});

//網頁主進入點
app.get('/', function(req, res){
//指定 /views/idex.ejs
res.render('index.ejs');
});

//顯示 posts.json 資料
app.get('/api/posts', function(req, res){
res.json(res.locals.posts);
});

//當 url 是 /post/:id 時, 取得某一筆資料
app.get('/post/:id', function(req, res, next){
//取得 post.json 資料夾
res.locals.posts.forEach(function(post){
//從 url 取得 id 參數與 posts.json 裡的 id
if (req.params.id === post.id){
//顯示參數為 url 中 id 的 post.id, 那麼顯示部分資料
res.render('post.ejs', { post: post });
}
})
});

app.listen(3000);
console.log('app is listening at localhost:3000...’);

再來是建立檢視資料夾 views,因為我們在 app.js 裡面設定樣板引擎是用 ejs,所以在 views 資料夾裡面新增三個檔案,分別是 header.ejs, index.ejs, footer.ejs

header.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><%= title %></title>
<link rel="stylesheet" href="/public/styles.css">
</head>
<body>

<header>
<div class="container">
<h1><a href="/"><%= title %></a></h1>
</div>
</header>

footer.ejs

<footer>
<div class="container">
This is footer side.
</div>
</footer>

</body>
</html>

index.ejs

<% include header %>

<main role="main">
<div class="container">
<h3><a href="/api/posts">/api/posts</a></h3>
<% posts.forEach(function(post){ %>
<h3>
<a href="/post/<%= post.id %>">
<%= post.title %>
</a>
</h3>
<div><%= post.content %></div>
<% }); %>
</main>
</div>

<% include footer %>

建立靜態資料

在 app.js 檔案裡有一行是

1
app.use('/public', express.static(__dirname + '/public'));

在做網站的過程,我們會建立 css,js 檔案,在根目錄下建立一個 public,並且在這個檔案增加一個 style.css,去定義一些樣式的方式,我這邊就不太多做 css 的解釋囉。

執行

1
$node index.js

結果

參考資料

http://expressjs.com/
http://expressjs.com/4x/api.html#app.locals
http://www.richyli.com/tool/loremipsum/