認識 Firebase 網頁版

1. 總覽

在本程式碼研究室中,您將瞭解 Firebase 的一些基本概念,以建立互動式網頁應用程式。您將使用多項 Firebase 產品建構活動回覆和留言板聊天應用程式。

這個步驟的螢幕截圖

課程內容

  • 使用 Firebase 驗證和 FirebaseUI 驗證使用者。
  • 使用 Cloud Firestore 同步處理資料。
  • 編寫 Firebase 安全性規則,保護資料庫安全。

軟硬體需求

  • 您選擇的瀏覽器,例如 Chrome。
  • 可存取 stackblitz.com (無需登入帳戶或登入)。
  • 擁有 Google 帳戶,例如 Gmail 帳戶。建議您選擇已用於 GitHub 帳戶的電子郵件帳戶。以便使用 StackBlitz 的進階功能。
  • 程式碼研究室的程式碼範例。請參閱下一個步驟,瞭解如何取得程式碼。

2. 取得範例程式碼

在本程式碼研究室中,您將使用 StackBlitz 建構應用程式,StackBlitz 是一個線上編輯器,它整合了數個 Firebase 工作流程。您不需要安裝軟體或建立特殊的 StackBlitz 帳戶,即可使用 Stackblitz。

您可以透過 StackBlitz 與他人共用專案。其他人只要有您的 StackBlitz 專案網址,就能查看您的程式碼並分支您的專案,但無法編輯您的 StackBlitz 專案。

  1. 前往這個網址取得起始程式碼:https://stackblitz.com/edit/firebase-gtk-web-start
  2. 按一下 StackBlitz 頁面頂端的「Fork」

這個步驟的螢幕截圖

您現在已複製了範例程式碼做為您自己的 StackBlitz 專案,此專案擁有獨特的名稱和專屬網址。所有檔案和變更都會儲存在這個 StackBlitz 專案中。

3. 編輯事件資訊

本程式碼研究室的起始教材提供網頁應用程式的部分結構,包括一些樣式表和應用程式的幾個 HTML 容器。在後續的程式碼研究室中,您會將這些容器連接至 Firebase。

首先,讓我們進一步瞭解 StackBlitz 介面。

  1. 在 StackBlitz 中開啟 index.html 檔案。
  2. 找出 event-details-containerdescription-container,然後嘗試編輯部分事件詳細資料。

編輯文字時,StackBlitz 會自動重新整理頁面,並顯示新的事件詳細資料。好,對吧?

<!-- ... -->

<div id="app">
  <img src="..." />

  <section id="event-details-container">
     <h1>Firebase Meetup</h1>

     <p><i class="material-icons">calendar_today</i> October 30</p>
     <p><i class="material-icons">location_city</i> San Francisco</p>

  </section>

  <hr>

  <section id="firebaseui-auth-container"></section>

  <section id="description-container">
     <h2>What we'll be doing</h2>
     <p>Join us for a day full of Firebase Workshops and Pizza!</p>
  </section>
</div>

<!-- ... -->

應用程式預覽畫面看起來應像這樣:

應用程式預覽

這個步驟的螢幕截圖

4. 建立及設定 Firebase 專案

顯示活動資訊對住客來說很有幫助,但只顯示活動資訊對任何人來說都沒什麼用處。現在我們要為這個應用程式新增一些動態功能。您需要將 Firebase 連結至應用程式。如要開始使用 Firebase,請建立並設定 Firebase 專案。

建立 Firebase 專案

  1. 登入 Firebase
  2. 在 Firebase 控制台中,按一下「新增專案」(或「建立專案」),然後將 Firebase 專案命名為「Firebase-Web-Codelab」

    這個步驟的螢幕截圖

  3. 點選專案建立選項。如果系統顯示提示,請接受 Firebase 條款。在 Google Analytics 畫面中按一下 [不允許啟用],因為您不會在這個應用程式中使用 Analytics。

如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」一文。

在控制台中啟用及設定 Firebase 產品

您建構的應用程式使用了多種適用於網頁應用程式的 Firebase 產品:

  • Firebase 驗證Firebase UI,讓使用者輕鬆登入您的應用程式。
  • Cloud Firestore,可將結構化資料儲存至雲端,並在資料變更時立即發送通知。
  • Firebase 安全性規則:可保護資料庫的安全。

部分產品需要特殊設定或透過 Firebase 控制台啟用。

為 Firebase 驗證啟用電子郵件登入功能

為了讓使用者登入網頁應用程式,請在本程式碼研究室中使用「電子郵件/密碼」登入方式:

  1. 在 Firebase 控制台的左側面板中,依序點選「Build」>「Authentication」。然後按一下「開始使用」。您現在已進入驗證資訊主頁,可以查看註冊的使用者、設定登入提供者,以及管理設定。

    這個步驟的螢幕截圖

  2. 選取「登入方式」分頁標籤 (或按這裡直接前往該分頁)。

    這個步驟的螢幕截圖

  3. 在供應商選項中按一下「電子郵件/密碼」,將切換鈕切換至「啟用」,然後按一下「儲存」

    這個步驟的螢幕截圖

設定 Cloud Firestore

這個網頁應用程式會使用 Cloud Firestore 儲存即時通訊訊息,並接收新的即時通訊訊息。

以下說明如何在 Firebase 專案中設定 Cloud Firestore:

  1. 在 Firebase 控制台的左側面板中,展開「Build」,然後選取 「Firestore database」
  2. 按一下 [Create database] (建立資料庫)。
  3. 保留「Database ID」(default)
  4. 選取資料庫的位置,然後按一下「Next」
    如果是實際應用程式,請選擇距離使用者較近的位置。
  5. 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
    在本程式碼研究室的後續部分,您將新增安全性規則來保護資料。請勿在未新增資料庫安全性規則的情況下公開發行或發布應用程式。
  6. 按一下「建立」

5. 新增及設定 Firebase

您現已建立 Firebase 專案並啟用部分服務,接下來需要指定要使用的 Firebase 程式碼和要使用的 Firebase 專案。

新增 Firebase 程式庫

如要讓應用程式使用 Firebase,您必須將 Firebase 程式庫新增至應用程式。如需詳細資訊,請參閱 Firebase 說明文件。舉例來說,您可以新增 Google CDN 的程式庫,也可以使用 npm 在本機安裝程式庫,然後使用 Browserify 將其封裝到應用程式中。

StackBlitz 提供自動捆綁功能,因此您可以使用匯入陳述式新增 Firebase 程式庫。您將使用模組化 (v9) 版本的程式庫,透過稱為「樹狀圖搖動」的程序,有助於縮減網頁的整體大小。如要進一步瞭解模組化 SDK,請參閱這份說明文件

如要建構這個應用程式,請使用 Firebase 驗證、FirebaseUI 和 Cloud Firestore 程式庫。在本程式碼研究室中,index.js 檔案的頂端已包含下列匯入陳述式,日後我們會從各個 Firebase 程式庫中匯入更多方法:

// Import stylesheets
import './style.css';

// Firebase App (the core Firebase SDK) is always required
import { initializeApp } from 'firebase/app';

// Add the Firebase products and methods that you want to use
import {} from 'firebase/auth';
import {} from 'firebase/firestore';

import * as firebaseui from 'firebaseui';

將 Firebase 網頁應用程式新增至 Firebase 專案

  1. 回到 Firebase 控制台,按一下左上方的「專案總覽」,前往專案總覽頁面。
  2. 在專案總覽頁面的中央,按一下網頁圖示 網頁應用程式圖示 建立新的 Firebase 網頁應用程式。

    這個步驟的螢幕截圖

  3. 使用「網頁應用程式」暱稱註冊應用程式。
  4. 在本程式碼研究室中,請勿勾選「Also set up Firebase Hosting for this app」旁的方塊。您目前會使用 StackBlitz 的預覽窗格。
  5. 按一下「註冊應用程式」

    這個步驟的螢幕截圖

  6. Firebase 設定物件複製到剪貼簿。

    這個步驟的螢幕截圖

  7. 按一下 [Continue to console]。在應用程式中新增 Firebase 設定物件:
  8. 回到 StackBlitz,前往 index.js 檔案。
  9. 找出 Add Firebase project configuration object here 註解行,然後將設定程式碼片段貼在註解正下方。
  10. 新增 initializeApp 函式呼叫,即可利用專屬的 Firebase 專案設定設定 Firebase。
    // ...
    // Add Firebase project configuration object here
    const firebaseConfig = {
      apiKey: "random-unique-string",
      authDomain: "your-projectId.firebaseapp.com",
      databaseURL: "https://your-projectId.firebaseio.com",
      projectId: "your-projectId",
      storageBucket: "your-projectId.firebasestorage.app",
      messagingSenderId: "random-unique-string",
      appId: "random-unique-string",
    };
    
    // Initialize Firebase
    initializeApp(firebaseConfig);
    

6. 新增使用者登入 (敬請回覆)

在應用程式中加入 Firebase 後,您可以設定回覆按鈕,讓使用者透過 Firebase 驗證功能註冊。

使用電子郵件登入和 FirebaseUI 驗證使用者

您需要回覆按鈕,提示使用者以電子郵件地址登入。您可以將 FirebaseUI 連結至 RSVP 按鈕。FirebaseUI 是可在 Firebase Auth 上提供預先建構 UI 的程式庫。

FirebaseUI 需要設定 (請參閱說明文件中的選項) 來執行以下兩項作業:

  • 告訴 FirebaseUI 您要使用「電子郵件/密碼」登入方式。
  • 處理成功登入的回呼,並傳回 false 以避免重新導向。您不希望頁面重新整理,因為您正在建構單頁面網頁應用程式。

新增用於初始化 FirebaseUI Auth 的程式碼

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 在頂端找到 firebase/auth 匯入陳述式,然後新增 getAuthEmailAuthProvider,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. 請在 initializeApp 後立即儲存對驗證物件的參照,如下所示:
    initializeApp(firebaseConfig);
    auth = getAuth();
    
  4. 請注意,範例程式碼中已提供 FirebaseUI 設定。您已採用電子郵件驗證服務供應商的設定。
  5. index.jsmain() 函式底部,新增 FirebaseUI 初始化陳述式,如下所示:
    async function main() {
      // ...
    
      // Initialize the FirebaseUI widget using Firebase
      const ui = new firebaseui.auth.AuthUI(auth);
    }
    main();
    
    

在 HTML 中新增 RSVP 按鈕

  1. 在 StackBlitz 中,前往 index.html 檔案。
  2. event-details-container 內新增 RSVP 按鈕的 HTML,如以下範例所示。

    請務必使用下方所示的 id 值,因為本程式碼研究室的 index.js 檔案中已為這些特定 ID 建立鉤子。

    請注意,index.html 檔案中有一個 ID 為 firebaseui-auth-container 的容器。這是您會傳遞至 FirebaseUI 的 ID,用於保存登入資訊。
    <!-- ... -->
    
    <section id="event-details-container">
        <!-- ... -->
        <!-- ADD THE RSVP BUTTON HERE -->
        <button id="startRsvp">RSVP</button>
    </section>
    <hr>
    <section id="firebaseui-auth-container"></section>
    <!-- ... -->
    
    應用程式預覽

    這個步驟的螢幕截圖

  3. 在 RSVP 按鈕上設定事件監聽器,並呼叫 FirebaseUI 啟動函式。這可以告知 FirebaseUI 您要查看登入視窗。

    將以下程式碼新增至 index.js 中的 main() 函式底部:
    async function main() {
      // ...
    
      // Listen to RSVP button clicks
      startRsvpButton.addEventListener("click",
       () => {
            ui.start("#firebaseui-auth-container", uiConfig);
      });
    }
    main();
    

測試登入應用程式

  1. 在 StackBlitz 的預覽視窗中,按一下「RSVP」按鈕即可登入應用程式。
    • 在本程式碼研究室中,您可以使用任何電子郵件地址,甚至是假的電子郵件地址,因為您未在本程式碼研究室中設定電子郵件驗證步驟。
    • 如果您看到顯示 auth/operation-not-allowedThe given sign-in provider is disabled for this Firebase project 的錯誤訊息,請確認您已在 Firebase 控制台中啟用「電子郵件/密碼」做為登入提供者。
    應用程式預覽

    這個步驟的螢幕截圖

  2. 前往 Firebase 主控台的「驗證資訊主頁。在「使用者」分頁中,您應該會看到登入應用程式時輸入的帳戶資訊。

    這個步驟的螢幕截圖

在 UI 中新增驗證狀態

接下來,請確認 UI 能反映您登入的資訊。

您需要使用 Firebase 驗證狀態事件監聽器回呼,每當使用者的登入狀態有所變更,就會收到通知。如果目前有登入的使用者,您的應用程式會將「回覆」按鈕切換為「登出」按鈕。

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 在頂端找到 firebase/auth 匯入陳述式,然後新增 signOutonAuthStateChanged,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. main() 函式的底部加入下列程式碼:
    async function main() {
      // ...
    
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
        } else {
          startRsvpButton.textContent = 'RSVP';
        }
      });
    }
    main();
    
  4. 在按鈕監聽器中,檢查並登出目前是否有使用者。如要這樣做,請將目前的 startRsvpButton.addEventListener 替換為以下內容:
    // ...
    // Called when the user clicks the RSVP button
    startRsvpButton.addEventListener('click', () => {
      if (auth.currentUser) {
        // User is signed in; allows user to sign out
        signOut(auth);
      } else {
        // No user is signed in; allows user to sign in
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    });
    

現在,應用程式中的按鈕應顯示「LOGOUT」,且在點選後應切換回「RSVP」

應用程式預覽

這個步驟的螢幕截圖

7. 將訊息寫入 Cloud Firestore

知道使用者一定會來到這裡固然很好,但現在要請訪客在應用程式中採取其他行動。如果他們可以在留言板中留下訊息,該怎麼辦?觀眾可以分享自己樂於助人的原因,或想認識哪些人。

如要儲存使用者在應用程式中輸入的即時通訊訊息,您可以使用 Cloud Firestore

資料模型

Cloud Firestore 是 NoSQL 資料庫,儲存在資料庫中的資料會分為集合、文件、欄位和子集合。您將每則即時通訊訊息儲存為文件,並儲存在名為 guestbook 的頂層集合中。

Firestore 資料模型圖表,顯示包含多個訊息文件的留言板集合

將訊息新增至 Firestore

在本節中,您將新增功能,讓使用者將新訊息寫入資料庫。首先,您要為 UI 元素 (訊息欄位和傳送按鈕) 新增 HTML。接著新增程式碼,將這些元素掛在資料庫。

如何新增訊息欄位和傳送按鈕的 UI 元素:

  1. 在 StackBlitz 中,前往 index.html 檔案。
  2. 找出 guestbook-container,然後加入下列 HTML,建立含有訊息輸入欄位和傳送按鈕的表單。
    <!-- ... -->
    
     <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form id="leave-message">
         <label>Leave a message: </label>
         <input type="text" id="message">
         <button type="submit">
           <i class="material-icons">send</i>
           <span>SEND</span>
         </button>
       </form>
    
     </section>
    
    <!-- ... -->
    

應用程式預覽

這個步驟的螢幕截圖

使用者按下「SEND」按鈕時,系統會觸發下方的程式碼片段。這會將訊息輸入欄位的內容新增至資料庫的 guestbook 集合。具體來說,addDoc 方法會將訊息內容新增至新文件 (含有自動產生的 ID) 至 guestbook 集合。

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 在頂端找到 firebase/firestore 匯入陳述式,然後新增 getFirestoreaddDoccollection,如下所示:
    // ...
    
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {
      getFirestore,
      addDoc,
      collection
    } from 'firebase/firestore';
    
  3. 現在,我們會在 initializeApp 之後儲存 Firestore db 物件的參照:
    initializeApp(firebaseConfig);
    auth = getAuth();
    db = getFirestore();
    
  4. main() 函式的底部加入下列程式碼。

    請注意,auth.currentUser.uid 是系統自動產生的專屬 ID 的參照,Firebase 驗證會將該 ID 提供給所有已登入的使用者。
    async function main() {
      // ...
    
      // Listen to the form submission
      form.addEventListener('submit', async e => {
        // Prevent the default form redirect
        e.preventDefault();
        // Write a new message to the database collection "guestbook"
        addDoc(collection(db, 'guestbook'), {
          text: input.value,
          timestamp: Date.now(),
          name: auth.currentUser.displayName,
          userId: auth.currentUser.uid
        });
        // clear message input field
        input.value = '';
        // Return false to avoid redirect
        return false;
      });
    }
    main();
    

只向已登入的使用者顯示留言板

你不希望只有任何人看到邀請對象的聊天室訊息。為確保即時通訊安全,您可以只允許已登入的使用者查看留言板。不過,如果是您自己的應用程式,建議您也使用 Firebase 安全性規則保護資料庫。(本程式碼研究室後續章節會進一步說明安全性規則)。

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 編輯 onAuthStateChanged 事件監聽器,隱藏及顯示留言板。
    // ...
    
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
      }
    });
    

測試傳送訊息

  1. 請確認你已登入該應用程式。
  2. 輸入訊息 (例如「嗨!」),然後按一下「傳送」

這項操作會將訊息寫入 Cloud Firestore 資料庫。不過,您還是不會在實際網頁應用程式中看到訊息,因為您還需要導入擷取資料。請在下一個步驟中執行這項操作。

不過您可以在 Firebase 控制台中查看新增的訊息。

在 Firebase 控制台的 Firestore 資料庫資訊主頁中,您應該會看到 guestbook 集合,其中包含您新加入的訊息。如果您持續傳送訊息,留言板集合就會包含許多文件,如下所示:

Firebase 控制台

這個步驟的螢幕截圖

8. 讀取訊息

同步處理郵件

雖然訪客可以將訊息寫入資料庫,但他們目前無法在應用程式中查看這些訊息。

如要顯示訊息,您需要新增資料變更時會觸發的事件監聽器,然後建立顯示新訊息的 UI 元素。

您需要加入程式碼,用於監聽應用程式新增的訊息。首先,在 HTML 中新增一個用來顯示訊息的程式碼:

  1. 在 StackBlitz 中,前往 index.html 檔案。
  2. guestbook-container 中新增 ID 為 guestbook 的新區段。
    <!-- ... -->
    
      <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form><!-- ... --></form>
    
       <section id="guestbook"></section>
    
     </section>
    
    <!-- ... -->
    

接下來,請註冊監聽器,監聽資料的變更:

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 在頂端找到 firebase/firestore 匯入陳述式,然後新增 queryorderByonSnapshot,如下所示:
    // ...
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot
    } from 'firebase/firestore';
    
  3. main() 函式的底部加入以下程式碼,以循環顯示資料庫中的所有文件 (留言板訊息)。如要進一步瞭解這個程式碼的運作方式,請參閱程式碼片段下方的資訊。
    async function main() {
      // ...
    
      // Create query for messages
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    main();
    

為了監聽資料庫中的訊息,您已使用 collection 函式針對特定集合建立查詢。上述程式碼會監聽 guestbook 集合中的變更,這是聊天訊息的儲存位置。訊息也會依日期排序,使用 orderBy('timestamp', 'desc') 將最新的訊息顯示在頂端。

onSnapshot 函式會採用兩個參數:要使用的查詢和回呼函式。如果符合查詢的文件有任何變更,就會觸發回呼函式。例如刪除、修改或新增訊息。詳情請參閱 Cloud Firestore 說明文件

測試同步處理訊息

Cloud Firestore 會自動立即將資料與訂閱資料庫的用戶端同步處理。

  • 您先前在資料庫中建立的訊息應該會顯示在應用程式中。歡迎您編寫新訊息,這些訊息應該會立即顯示。
  • 如果您在多個視窗或分頁中開啟工作區,訊息會即時同步處理至不同分頁。
  • (選用) 您可以試著直接在 Firebase 控制台的「資料庫」部分手動刪除、修改或新增訊息;任何變更都會顯示在 UI 中。

恭喜!您正在應用程式讀取 Cloud Firestore 文件!

應用程式預覽

這個步驟的螢幕截圖

9. 設定基本安全性規則

您一開始設定 Cloud Firestore 時,會使用測試模式,也就是說資料庫會開放讀取和寫入作業。不過,您應該只在開發過程的初期階段使用測試模式。最佳做法是在開發應用程式時為資料庫設定安全性規則。安全性必須對應用程式的結構和行為至關重要。

您可以使用安全性規則控管資料庫中文件和集合的存取權。您可以使用彈性的規則語法,建立可比對所有寫入作業的規則,包括對整個資料庫的寫入作業,以及對特定文件的作業。

您可以在 Firebase 控制台中編寫 Cloud Firestore 的安全性規則:

  1. 在 Firebase 控制台的「建構」部分按一下「Firestore 資料庫」,然後選取「規則」分頁標籤 (或按這裡直接前往「規則」分頁)。
  2. 畫面上應會顯示下列預設安全性規則,設有公開存取時間限制,且從今天起的數週後即生效。

這個步驟的螢幕截圖

找出集合

首先,請找出應用程式寫入資料的集合。

  1. 刪除現有的 match /{document=**} 子句,讓規則如下所示:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
      }
    }
    
  2. match /databases/{database}/documents 中,找出要保護的集合:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
         // You'll add rules here in the next step.
      }
    }
    

新增安全性規則

由於您在每組留言板文件中使用驗證 UID 做為欄位,因此您可以取得驗證 UID,並驗證所有嘗試寫入該文件的使用者都擁有相符的驗證 UID。

  1. 為您的規則組合新增讀取和寫入規則,如下所示:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
            if request.auth.uid == request.resource.data.userId;
        }
      }
    }
    
  2. 按一下「發布」即可部署新規則。現在,只有登入的使用者可以閱讀留言板上的訊息 (任何訊息),但您只能使用使用者 ID 建立訊息。我們也不允許編輯或刪除訊息。

新增驗證規則

  1. 新增資料驗證,確保文件中包含所有預期的欄位:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
          if request.auth.uid == request.resource.data.userId
              && "name" in request.resource.data
              && "text" in request.resource.data
              && "timestamp" in request.resource.data;
        }
      }
    }
    
  2. 按一下「發布」即可部署新規則。

重設事件監聽器

由於您的應用程式現在僅允許經過驗證的使用者登入,因此您應該在驗證事件監聽器內移動留言板 firestore 查詢。否則會發生權限錯誤,且在使用者登出時,應用程式會中斷連線。

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 將留言板收集 onSnapshot 事件監聽器拉入名為 subscribeGuestbook 的新函式。此外,請將 onSnapshot 函式的結果指派至 guestbookListener 變數。

    Firestore onSnapshot 監聽器會「傳回」取消訂閱函式,讓您日後可以取消快照事件監聽器。
    // ...
    // Listen to guestbook updates
    function subscribeGuestbook() {
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      guestbookListener = onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    
  3. unsubscribeGuestbook 下方新增函式。檢查 guestbookListener 變數是否非空值,然後呼叫函式取消事件監聽器。
    // ...
    // Unsubscribe from guestbook updates
    function unsubscribeGuestbook() {
      if (guestbookListener != null) {
        guestbookListener();
        guestbookListener = null;
      }
    }
    

最後,將新函式新增至 onAuthStateChanged 回呼。

  1. if (user) 底部新增 subscribeGuestbook()
  2. else 陳述式底部新增 unsubscribeGuestbook()
    // ...
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
        // Subscribe to the guestbook collection
        subscribeGuestbook();
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
        // Unsubscribe from the guestbook collection
        unsubscribeGuestbook();
      }
    });
    

10. 額外步驟:練習所學內容

記錄與會者的出席回覆狀態

目前,您的應用程式只允許使用者在對活動感興趣時開始即時通訊。此外,如果有人在即時通訊中發文,你就會知道對方是否會出席。讓我們來安排一下,讓大家知道有多少人會來。

您將新增切換鈕,讓使用者註冊參加活動,然後收集參與人數。

  1. 在 StackBlitz 中,前往 index.html 檔案。
  2. guestbook-container 中,新增一組「是」和「否」按鈕,如下所示:
    <!-- ... -->
      <section id="guestbook-container">
       <h2>Are you attending?</h2>
         <button id="rsvp-yes">YES</button>
         <button id="rsvp-no">NO</button>
    
       <h2>Discussion</h2>
    
       <!-- ... -->
    
     </section>
    <!-- ... -->
    

應用程式預覽

這個步驟的螢幕截圖

接下來,註冊按鈕點擊的監聽器。如果使用者點選「是」,系統會使用其驗證 UID 將回應儲存至資料庫。

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. 在頂端找到 firebase/firestore 匯入陳述式,然後新增 docsetDocwhere,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot,
      doc,
      setDoc,
      where
    } from 'firebase/firestore';
    
  3. main() 函式的底部,新增以下程式碼來監聽回覆狀態:
    async function main() {
      // ...
    
      // Listen to RSVP responses
      rsvpYes.onclick = async () => {
      };
      rsvpNo.onclick = async () => {
      };
    }
    main();
    
    
  4. 接下來,請建立名為 attendees 的新集合。當使用者按下 [回覆] 按鈕時,就會登錄文件參照。根據點選的按鈕,將參照設為 truefalse

    首先,針對 rsvpYes
    // ...
    // Listen to RSVP responses
    rsvpYes.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attendi()ng: true
      try {
        await setDoc(userRef, {
          attending: true
        });
      } catch (e) {
        console.error(e);
      }
    };
    
    然後,rsvpNo 相同,但值為 false
    rsvpNo.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attending: true
      try {
        await setDoc(userRef, {
          attending: false
        });
      } catch (e) {
        console.error(e);
      }
    };
    

更新安全性規則

由於您已設定部分規則,因此您使用這些按鈕新增的新資料將會遭拒。

允許新增至 attendees 集合

您必須更新規則,才能新增至 attendees 集合。

  1. 至於 attendees 集合,由於您是使用驗證 UID 做為文件名稱,因此您可以擷取該 UID,並確認提交者的 uid 與他們撰寫的文件相同。由於沒有私人資料,因此所有使用者都能查看與會者名單,但只有建立者才能更新。
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId;
        }
      }
    }
    
  2. 按一下「發布」,即可部署新規則。

新增驗證規則

  1. 新增一些資料驗證規則,確保文件中包含所有預期的欄位:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId
              && "attending" in request.resource.data;
    
        }
      }
    }
    
  2. 別忘了點選「發布」來部署規則!

(選用) 您現在可以查看點選按鈕的結果。前往 Firebase 主控台的 Cloud Firestore 資訊主頁。

讀取回覆狀態

現在,您已經記錄了回應,讓我們看看有哪些參與者,並在使用者介面中反映。

  1. 在 StackBlitz 中,前往 index.html 檔案。
  2. description-container 中,新增 ID 為 number-attending 的元素。
    <!-- ... -->
    
     <section id="description-container">
         <!-- ... -->
         <p id="number-attending"></p>
     </section>
    
    <!-- ... -->
    

接著,請為 attendees 集合註冊事件監聽器,並計算「YES」回應的數量:

  1. 在 StackBlitz 中,前往 index.js 檔案。
  2. main() 函式的底部加入以下程式碼,即可監聽回覆狀態並計算 YES 點擊次數。
    async function main() {
      // ...
    
      // Listen for attendee list
      const attendingQuery = query(
        collection(db, 'attendees'),
        where('attending', '==', true)
      );
      const unsubscribe = onSnapshot(attendingQuery, snap => {
        const newAttendeeCount = snap.docs.length;
        numberAttending.innerHTML = newAttendeeCount + ' people going';
      });
    }
    main();
    

最後,我們來醒目顯示對應於目前狀態的按鈕。

  1. 建立函式,檢查目前驗證 UID 是否在 attendees 集合中含有項目,然後將按鈕類別設為 clicked
    // ...
    // Listen for attendee list
    function subscribeCurrentRSVP(user) {
      const ref = doc(db, 'attendees', user.uid);
      rsvpListener = onSnapshot(ref, doc => {
        if (doc && doc.data()) {
          const attendingResponse = doc.data().attending;
    
          // Update css classes for buttons
          if (attendingResponse) {
            rsvpYes.className = 'clicked';
            rsvpNo.className = '';
          } else {
            rsvpYes.className = '';
            rsvpNo.className = 'clicked';
          }
        }
      });
    }
    
  2. 接著我們來提供取消訂閱的功能。系統會在使用者登出時使用。
    // ...
    function unsubscribeCurrentRSVP() {
      if (rsvpListener != null) {
        rsvpListener();
        rsvpListener = null;
      }
      rsvpYes.className = '';
      rsvpNo.className = '';
    }
    
  3. 從驗證事件監聽器呼叫函式。
    // ...
    // Listen to the current Auth state
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
          // Show guestbook to logged-in users
          guestbookContainer.style.display = 'block';
    
          // Subscribe to the guestbook collection
          subscribeGuestbook();
          // Subscribe to the user's RSVP
          subscribeCurrentRSVP(user);
        } else {
          startRsvpButton.textContent = 'RSVP';
          // Hide guestbook for non-logged-in users
          guestbookContainer.style.display = 'none'
          ;
          // Unsubscribe from the guestbook collection
          unsubscribeGuestbook();
          // Unsubscribe from the guestbook collection
          unsubscribeCurrentRSVP();
        }
      });
    
  4. 嘗試以多位使用者登入,查看每多按一下 YES 按鈕,數量就會增加。

應用程式預覽

這個步驟的螢幕截圖

11. 恭喜!

您已使用 Firebase 建構互動式的即時網頁應用程式!

涵蓋內容

  • Firebase 驗證
  • FirebaseUI
  • Cloud Firestore
  • Firebase 安全性規則

後續步驟

瞭解詳情

成果如何?

歡迎提供意見!請在這裡填寫非常簡短的表單。