前陣子處理一個報名網站的專案,是利用剛學完的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, ...}); }
|
- 透過
db.collection(collection名稱).add(...)
來加入資料
- 這個函數傳入的參數
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的語法:
<%=變數%>
會把變數render到前端,提供給前端該變數的值
<%程式語法%>
舉例來說,<%for(var i=0;i<rest_num.length;i++){%>
等同於這行用for(var i=0;i<rest_num.length;i++){
迴圈來執行程式碼。