NUK JavaScript Lesson 11:Express 與 Firestore 整合使用

介紹了 Express 與 Firestore 的使用後,我們來複習一次,並試著整合這兩個服務來完成一些有趣的功能吧。

環境建置

Step 1:架設 Express 環境

在 Node.js 下執行以下指令:

  1. npm install express-generator -g
  2. express --view=ejs
  3. npm install
  4. npm start
  5. npm install firebase-admin --save

Add the Firebase Admin SDK to Your Server

完成後開啟 http://localhost:3000/ 查看是否安裝成功,順利的話會出現以下結果:

Express

Step 2:載入 Firestore 到 Node.js

  • 進入 Firebase 的專案設定中產生金鑰

Firebase

  • 下載金鑰後,將金鑰放在根目錄並載入 Node.js
1
2
3
4
5
6
7
8
const admin = require('firebase-admin');
let serviceAccount = require('../您的金鑰.json');

admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});

let db = admin.firestore();

完成以上步驟,金鑰就載入成功囉。

語法說明

Get & Post

  • router.get:取得資料。
  • router.post:使用者新增資料,傳資料到路徑 (form post)。
    例如:送出的位置是 /addStudent (action)。

req

  • req.body:可以抓到使用者從前台傳送過來的數值,通常用 “物件” 的方式傳進來。
    例如:假設 req.body({name: "小明", no: "12"}),那麼 req.body.name 就是 小明 這個值。這個 req.body.namename 就是對應 EJS <input>name 屬性。

常見 res 顯示

  • res.render:顯示網頁 EJS。
    通常用於 Get 取得資料列表後,用 render 渲染該指定頁面的 EJS。
    例如:

    1
    2
    3
    4
    res.render('product', {
    title: '產品列表',
    data: productsData,
    });
  • res.redirect:要 “轉址” 到哪裡。
    例如:透過 Post 傳遞資料後,跳轉至 /students 這個 EJS 頁面。

  • res.send():傳送文字,也可以回傳 JSON 格式,通常是在 “不希望換頁” 的情況下會使用到。
    例如:res.send({"name": "mary"})

範例練習

功能介紹:

  • 顯示 2300 元以下的產品:/products?at=2300
  • 顯示 3 筆資料:/products?limit=3
  • 關鍵字搜尋:/products?keyword=2300
  • 混合使用:/products?keyword=2300&limit=3&at=2300
    (搜尋產品名稱含有 “2300”,且價格不高於 2300 元的產品。搜尋結果以價格來做降冪排序,並限制最多只顯示三筆資料)

index.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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var express = require('express');
var router = express.Router();

// 載入 Firestore 到 Node.js
const admin = require('firebase-admin');
let serviceAccount = require('../我的金鑰.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});

// 初始化 firebase
let db = admin.firestore();

// 首頁
router.get('/', function (req, res, next) {
console.log(db); // 檢查有沒有載入成功
res.render('index', {
title: 'Express',
});
});

// 產品頁,網址列可做各種查詢功能
router.get('/products', function (req, res, next) {
var docRef = db.collection('products2');
let productsData = [];
// 價格查詢
let at = parseInt(req.query.at) || 100000000;
// 限制數量
let limit = parseInt(req.query.limit) || 10;

// 判斷式
docRef
.where('price', '<=', at)
.orderBy('price', 'desc')
.limit(limit)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
// 關鍵字搜尋
if (doc.data().name.includes(req.query.keyword)) {
console.log(doc.data().name, req.query.keyword);
productsData.push(doc.data());
}
});
res.render('product', {
title: '產品列表',
data: productsData,
});
});
});

// 新增產品,傳資料到路徑 (表單傳送到後台)
router.post('/addProduct', function (req, res, next) {
var docRef = db.collection('products2');
docRef
.add({
name: req.body.name,
price: req.body.price,
})
.then(function () {
res.redirect('/products');
});
});

// End
module.exports = router;

上面針對不同的查詢功能,都有先宣告一個預設值,這個預設值的用途,是當使用者未使用該項查詢,就帶入預設值做判斷。

例如:以價格查詢功能來說,若使用者並未輸入網址 /products?at= 來做查詢,則會預設帶入 100000000 這個數字做 .where("price", "<=", at) 的判斷並呈現結果。

這邊之所以額外再寫一個 if 做關鍵字的判斷,是因為發現寫兩個 .where() 會導致它出現找不到值的錯誤。

product.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<!-- 輸入表單 -->
<form method="post" action="/addProduct">
<input name="name" value="" placeholder="輸入產品名稱">
<input type="number" name="price" value="" placeholder="輸入價格">
<input type="submit" value="送出">
</form>
<!-- 使用 for 迴圈顯示資料庫所有資料 -->
<% for(let i =0;data.length>i;i++){ %>
<li><%- data[i].name %> - <%- data[i].price %></li>
<% } %>
</body>
</html>

在 EJS 裡面,如果要使用資料,會用到 <%- 資料 -%> 的語法。
而表單的部分也要特別注意,例如:將價格的輸入型別 type 改為 number,以符合資料庫的格式。

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫。