MaDi's Blog

一個紀錄自己在轉職軟體工程師路上的學習小空間

0%

[Node.js+Firebase] 報名網站專案心得

前陣子處理一個報名網站的專案,是利用剛學完的Node.js來做後端處理,資料庫採用Firebase,為了怕自己太常寫python而生疏Node.js,因此記錄了這篇表單傳送資料的心得以供未來的自己參考。

下載模組

1
2
3
4
npm init
npm install --save express
npm install --save ejs
npm install --save firebase

app.js (主程式)

1
2
3
4
5
6
7
8
9
10
let express = require('express');  //引入Express

var bodyParser = require('body-parser');
app.use(bodyParser.json()); //內容支援 JSON 格式
var urlencodedParser = bodyParser.urlencoded({ extended: true });

let app = express(); // 建立server
app.use(express.static('./public'));
const path = require('path'); //使用path套件來處理路徑問題!
const maxNum = 18; //設定報名人數上限

設定Firebase資料庫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let firebase = require('firebase'); //引入firebase

var firebaseConfig = {
apiKey:... ,
authDomain:...,
databaseURL:...,
projectId:...,
storageBucket:...,
messagingSenderId:...,
appId:...
};

firebase.initializeApp(firebaseConfig); //Initialize Firebase
// firebase.analytics();
const db = firebase.firestore();

定義一個非同步的函式fetchData來取得資料庫裏頭的資料,Firebase裏頭的collection名稱是Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function fetchData(db) {
let peopleArr = [];
await db.collection('Demo').get().then(data => {
data.forEach((doc) => {
peopleArr.push({
name: doc.data().name,
ID_number: doc.data().ID_number,
birthday: doc.data().birthday,
contact_number: doc.data().contact_number,
email: doc.data().email,
...})
})
})
return peopleArr;
}

其中,db.collection回傳的資料可以直接迴圈找出每個doc的資料,利用doc.data().資料變數名稱來取得資料

定義一個非同步的函式addData來把新的資料存入資料庫,Firebase裏頭的collection名稱是Demo

1
2
3
4
5
6
7
8
9
async function addData(db, body) {
await db.collection('Demo').add({
name: body.name,
ID_number: body.ID_number,
birthday: body.birthday,
contact_number: body.contact_number,
email: body.email,
...});
}
  1. 透過db.collection(collection名稱).add(...)來加入資料
  2. 這個函數傳入的參數body其實是req.body,也就是表單post過來的資訊,透過解析req.body.資料變數名稱即可取得post過來的資料

宣告要渲染(render)的view engine是ejs樣板

1
app.set('view engine', 'ejs');

當HTTP以get方式要求網頁,將資料從firebase撈出來並render到前端,網頁檔名為views_demo.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
app.get("/", async (req, res) => {
peopleArr = await fetchData(db); //取得firebase裏頭的資料
var rest_num = [0, 1, 2]; //要render到前端的變數

//render到前端,夾帶變數num, maxNum...
res.render(
'views_demo', {
num: peopleArr.length,
maxNum: maxNum,
rest_num: rest_num,
...
})
})

當HTTP以post方式要求網頁,將表單輸入的資料傳到後端

1
2
3
4
5
6
7
8
9
10
app.post('/send', urlencodedParser, async (req, res) => {
peopleArr = await fetchData(db); //取得firebase裏頭的資料
if (peopleArr.length >= maxNum) {
res.end(); //報名結束
}
else {
await addData(db, req.body);
res.end();
}
});

對於所有網址都找不到的時候

1
2
3
app.get('*', (req, res) => {
res.send('No Content');
});

當不確定port是否會在8080(從測試版publish到正式版的時候要加上)

1
let port = process.env.PORT || 8080

監聽在port

1
2
3
app.listen(port, () => {
console.log(`Server is running at ${port}`)
});

views_demo.ejs(渲染的前端)

1
2
3
4
// 表單
<form action="/send" style='display:none' method="post" id="enrollForm" class="php-email-form" data-toggle="validator">
...
</form>

action="/send",因為在post的方法中,也是把Path設成”/send”,兩個目的地要一樣才收得到。

後端渲染到前端render變數的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//表單裏頭其中一塊項目
<div class="row">
<div class="form-group col-8 col-md-6">
<select class="form-control" name="testRide" id="testRide" data-rule='required' data-msg='請選擇'
value=<%=rest_num%>>
<option value='' selected>請選擇</option>
<option value='0'>11/18 14:30-15:00(剩餘<%=rest_num[0]%>人)
</option>
<option value='1'>11/18 15:10-15:40(剩餘<%=rest_num[1]%>人)
</option>
<option value='2'>11/18 15:50-16:20(剩餘<%=rest_num[2]%>人)
</option>
<option value='no'>不參加</option>
</select>
<div class="validate"></div>
</div>
</div>

render的語法:

  1. <%=變數%>
    會把變數render到前端,提供給前端該變數的值
  2. <%程式語法%>
    舉例來說,<%for(var i=0;i<rest_num.length;i++){%>等同於這行用for(var i=0;i<rest_num.length;i++){迴圈來執行程式碼。