了解 Firebase 網頁版

1. 概述

在此 Codelab 中,您將學習Firebase建立互動式 Web 應用程式的一些基礎知識。您將使用多種 Firebase 產品建立活動 RSVP 和留言簿聊天應用程式。

此步驟的螢幕截圖

你將學到什麼

  • 使用 Firebase 身份驗證和 FirebaseUI 對使用者進行身份驗證。
  • 使用 Cloud Firestore 同步資料。
  • 編寫 Firebase 安全規則來保護資料庫。

你需要什麼

  • 您選擇的瀏覽器,例如 Chrome。
  • 造訪stackblitz.com (無需帳戶或登入)。
  • Google 帳戶,例如 Gmail 帳戶。我們建議您使用已用於 GitHub 帳戶的電子郵件帳戶。這允許您使用 StackBlitz 中的高級功能。
  • Codelab 的範例程式碼。請參閱下一步以了解如何取得程式碼。

2.獲取起始碼

在此 Codelab 中,您將使用StackBlitz建立一個應用,這是一個整合了多個 Firebase 工作流程的線上編輯器。 Stackblitz 不需要安裝軟體或特殊的 StackBlitz 帳號。

StackBlitz 可讓您與其他人分享專案。擁有您的 StackBlitz 項目 URL 的其他人可以查看您的程式碼並分叉您的項目,但他們無法編輯您的 StackBlitz 專案。

  1. 前往此 URL 取得起始程式碼: https://stackblitz.com/edit/firebase-gtk-web-start
  2. 在 StackBlitz 頁面頂部,按一下Fork

此步驟的螢幕截圖

您現在擁有了一份起始程式碼的副本作為您自己的 StackBlitz 項目,該項目具有唯一的名稱和唯一的 URL。您的所有檔案和變更都保存在此 StackBlitz 專案中。

3.編輯活動資訊

此 Codelab 的起始材料提供了 Web 應用程式的一些結構,包括應用程式的一些樣式表和幾個 HTML 容器。稍後在此 Codelab 中,您會將這些容器連接到 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 身份驗證

為了允許使用者登入 Web 應用,您將在此 Codelab 中使用電子郵件/密碼登入方法:

  1. 在 Firebase 控制台的左側面板中,按一下「建置」 > “驗證” 。然後單擊開始。現在您位於身份驗證儀表板中,您可以在其中查看註冊用戶、配置登入提供者以及管理設定。

    此步驟的螢幕截圖

  2. 選擇「登入方法」標籤(或按一下此處直接前往該分頁)。

    此步驟的螢幕截圖

  3. 按一下提供者選項中的電子郵件/密碼,將開關切換至啟用,然後按一下儲存

    此步驟的螢幕截圖

設定 Cloud Firestore

Web 應用程式使用Cloud Firestore儲存聊天訊息並接收新的聊天訊息。

設定 Cloud Firestore 的方法如下:

  1. 在 Firebase 控制台的左側面板中,按一下「建置」 > “Firestore 資料庫” 。然後點擊建立資料庫
  2. 按一下建立資料庫

    此步驟的螢幕截圖

  3. 選擇“以測試模式啟動”選項。閱讀有關安全規則的免責聲明。測試模式保證您在開發過程中可以自由寫入資料庫。點擊下一步

    此步驟的螢幕截圖

  4. 選擇資料庫的位置(您可以使用預設值)。但請注意,此位置以後無法更改。

    此步驟的螢幕截圖

  5. 按一下“完成”

5.新增並配置Firebase

現在您已經建立了 Firebase 專案並啟用了一些服務,您需要告訴程式碼您要使用 Firebase,以及要使用哪個 Firebase 專案。

新增 Firebase 庫

為了讓您的應用程式使用 Firebase,您需要將 Firebase 程式庫新增至應用程式。有多種方法可以執行此操作,如Firebase 文件 中所述。例如,您可以從 Google 的 CDN 新增庫,也可以使用 npm 在本機安裝它們,然後將它們打包到您的應用程式中(如果您使用的是 Browserify)。

StackBlitz 提供自動捆綁,因此您可以使用 import 語句新增 Firebase 程式庫。您將使用庫的模組化 (v9) 版本,這有助於透過稱為「tree shake」的過程來減少網頁的整體大小。您可以在文件中了解有關模組化 SDK 的更多資訊。

若要建置此應用程式,您可以使用 Firebase 驗證、FirebaseUI 和 Cloud Firestore 程式庫。對於此 Codelab, 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 Web 應用程式新增至您的 Firebase 項目

  1. 返回 Firebase 控制台,點擊左上角的項目概述導覽至專案的概述頁面。
  2. 在專案概述頁面的中心,按一下 Web 圖標網路應用程式圖標建立一個新的 Firebase Web 應用程式。

    此步驟的螢幕截圖

  3. 使用暱稱Web App註冊該應用程式。
  4. 對於此 Codelab,請勿選取Also set up Firebase Hosting for this app旁的複選框。您現在將使用 StackBlitz 的預覽窗格。
  5. 點擊註冊應用程式

    此步驟的螢幕截圖

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

    此步驟的螢幕截圖

  7. 按一下繼續控制台。將 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.appspot.com",
      messagingSenderId: "random-unique-string",
      appId: "random-unique-string",
    };
    
    // Initialize Firebase
    initializeApp(firebaseConfig);
    

6.新增用戶登入(RSVP)

現在您已將 Firebase 新增至應用程式中,您可以設定一個 RSVP 按鈕來使用Firebase 驗證註冊人員。

使用電子郵件登入和 FirebaseUI 對您的使用者進行身份驗證

您需要一個 RSVP 按鈕來提示使用者使用其電子郵件地址登入。您可以透過將FirebaseUI連接到 RSVP 按鈕來完成此操作。FirebaseUI 是一個庫,它為您提供基於 Firebase Auth 的預先建置 UI。

FirebaseUI 需要一個設定(請參閱文件中的選項)來執行兩件事:

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

新增程式碼以初始化 FirebaseUI Auth

  1. 在 StackBlitz 中,前往index.js檔。
  2. 在頂部,找到firebase/auth import 語句,然後加入getAuthEmailAuthProvider ,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. initializeApp之後儲存auth物件的引用,如下所示:
    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值,因為對於此 Codelab, 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 按鈕登入應用程式。
    • 對於此 Codelab,您可以使用任何電子郵件地址,甚至可以使用虛假電子郵件地址,因為您沒有為此 Codelab 設定電子郵件驗證步驟。
    • 如果您看到錯誤訊息,指出auth/operation-not-allowedThe given sign-in provider is disabled for this Firebase project ,請檢查以確保您在 Firebase 控制台中啟用了電子郵件/密碼作為登入提供程序。
    應用程式預覽

    此步驟的螢幕截圖

  2. 前往 Firebase 控制台中的身份驗證儀表板。在「使用者」標籤中,您應該會看到您輸入的用於登入應用程式的帳戶資訊。

    此步驟的螢幕截圖

將身份驗證狀態新增至 UI

接下來,請確保 UI 反映您已登入的事實。

您將使用 Firebase 驗證狀態偵聽器回調,只要使用者的登入狀態發生變化,該回呼就會收到通知。如果目前有登入用戶,您的應用程式會將「RSVP」按鈕切換為「登出」按鈕。

  1. 在 StackBlitz 中,前往index.js檔。
  2. 在頂部,找到firebase/auth import 語句,然後加入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>
    
    <!-- ... -->
    

應用程式預覽

此步驟的螢幕截圖

使用者點擊「傳送」按鈕將觸發下面的程式碼片段。它將訊息輸入欄位的內容新增到資料庫的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是對 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 安全性規則來保護您的資料庫。 (稍後將在 Codelab 中提供有關安全規則的更多資訊。)

  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 資料庫。但是,您還不會在實際的 Web 應用程式中看到該訊息,因為您仍然需要實作資料擷取。接下來你會這樣做。

但您可以在 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 import 語句,然後加入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 Database ,然後選擇「規則」標籤(或按一下此處直接前往「規則」標籤)。
  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. 將 guestbook 集合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變數是否不為null,然後呼叫函數取消監聽。
    // ...
    // 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.獎勵步驟:練習你所學到的知識

記錄與會者的 RSVP 狀態

目前,您的應用程式只允許人們在對活動感興趣的情況下開始聊天。此外,您知道某人是否會來的唯一方法是他們是否在聊天中發布。讓我們組織起來,讓人們知道有多少人來。

您將新增一個開關來註冊想要參加活動的人員,然後收集參加活動的人數。

  1. 在 StackBlitz 中,前往index.html檔。
  2. guestbook-container中,新增一組YESNO按鈕,如下所示:
    <!-- ... -->
      <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>
    <!-- ... -->
    

應用程式預覽

此步驟的螢幕截圖

接下來,註冊按鈕點擊的監聽器。如果使用者按一下YES ,則使用其驗證 UID 將回應儲存到資料庫。

  1. 在 StackBlitz 中,前往index.js檔。
  2. 在頂部,找到firebase/firestore import 語句,然後加入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()函數的底部,加入以下程式碼來監聽RSVP狀態:
    async function main() {
      // ...
    
      // Listen to RSVP responses
      rsvpYes.onclick = async () => {
      };
      rsvpNo.onclick = async () => {
      };
    }
    main();
    
    
  4. 接下來,建立一個名為attendees的新集合,然後在點擊 RSVP 按鈕時註冊文件引用。根據單擊的按鈕將該引用設為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是否與他們正在編寫的文檔相同。您將允許每個人讀取與會者清單(因為那裡沒有私人資料),但只有創建者才能更新它。
    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 儀表板。

讀取回覆狀態

現在您已經記錄了回應,讓我們看看誰來了並將其反映在 UI 中。

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

接下來,為attendees集合註冊偵聽器併計算「是」回應的數量:

  1. 在 StackBlitz 中,前往index.js檔。
  2. main()函數的底部,加入以下程式碼來監聽 RSVP 狀態並計算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. 嘗試以多個用戶身份登錄,並查看每次單擊“是”按鈕時計數都會增加。

應用程式預覽

此步驟的螢幕截圖

11. 恭喜!

您已使用 Firebase 建立了互動式即時 Web 應用程式!

我們涵蓋的內容

  • Firebase 身份驗證
  • Firebase使用者介面
  • 雲端Firestore
  • Firebase 安全規則

下一步

  • 想了解更多 Firebase 開發人員工作流程嗎?請查看Firebase 模擬器 Codelab,了解如何完全在本地測試和運行您的應用程式。
  • 想要詳細了解其他 Firebase 產品嗎?也許您想儲存用戶上傳的圖像檔案?或向您的用戶發送通知?請參閱Firebase Web Codelab ,了解更深入了解更多 Firebase Web 產品的程式碼實驗室。
  • 想要了解更多關於 Cloud Firestore 的資訊?也許您想了解子集合和交易?前往Cloud Firestore Web Codelab,以獲得更深入了解 Cloud Firestore 的 Codelab。或查看此YouTube 系列以了解 Cloud Firestore

了解更多

進展如何?

我們很樂意您的回饋!請在此處填寫一份(非常)簡短的表格。