了解 Firebase 網頁版

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

一、概述

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

此步驟的屏幕截圖

你會學到什麼

  • 使用 Firebase 身份驗證和 FirebaseUI 對用戶進行身份驗證。
  • 使用 Cloud Firestore 同步數據。
  • 編寫 Firebase 安全規則以保護數據庫。

你需要什麼

  • 您選擇的瀏覽器,例如 Chrome。
  • 訪問stackblitz.com (無需帳戶或登錄)。
  • Google 帳戶,例如 gmail 帳戶。我們建議您使用已用於 GitHub 帳戶的電子郵件帳戶。這允許您使用 StackBlitz 中的高級功能。
  • Codelab 的示例代碼。有關如何獲取代碼,請參閱下一步。

2.獲取起始碼

在此 Codelab 中,您將使用StackBlitz構建一個應用,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 控制台中,點擊Add Project (或Create a project ),然後將您的 Firebase 項目命名為Firebase-Web-Codelab

    此步驟的屏幕截圖

  3. 單擊項目創建選項。如果出現提示,請接受 Firebase 條款。在 Google Analytics(分析)屏幕上,單擊“不啟用”,因為您不會為此應用程序使用 Analytics。

要詳細了解 Firebase 項目,請參閱了解 Firebase 項目

在控制台中啟用和設置 Firebase 產品

您正在構建的應用使用了多個可用於網絡應用的 Firebase 產品:

  • Firebase 身份驗證Firebase 用戶界面可讓您的用戶輕鬆登錄您的應用。
  • Cloud Firestore將結構化數據保存在雲端,並在數據更改時獲得即時通知。
  • Firebase 安全規則來保護您的數據庫。

其中一些產品需要特殊配置或需要使用 Firebase 控制台啟用。

為 Firebase 身份驗證啟用電子郵件登錄

要允許用戶登錄 Web 應用,您將為此 Codelab 使用電子郵件/密碼登錄方法:

  1. 在 Firebase 控制台的左側面板中,單擊Build > Authentication 。然後單擊開始。您現在位於身份驗證儀表板中,您可以在其中查看註冊用戶、配置登錄提供程序和管理設置。

    此步驟的屏幕截圖

  2. 選擇登錄方法選項卡(或單擊此處直接轉到該選項卡)。

    此步驟的屏幕截圖

  3. 單擊提供商選項中的電子郵件/密碼,將開關切換到啟用,然後單擊保存

    此步驟的屏幕截圖

設置 Cloud Firestore

該網絡應用使用Cloud Firestore來保存聊天消息並接收新的聊天消息。

以下是設置 Cloud Firestore 的方法:

  1. 在 Firebase 控制台的左側面板中,點擊Build > Firestore Database 。然後單擊創建數據庫
  2. 單擊創建數據庫

    此步驟的屏幕截圖

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

    此步驟的屏幕截圖

  4. 選擇數據庫的位置(您可以使用默認位置)。但請注意,此位置以後無法更改。

    此步驟的屏幕截圖

  5. 單擊完成

5. 添加並配置 Firebase

現在您已經創建了 Firebase 項目並啟用了一些服務,您需要告訴代碼您要使用 Firebase,以及要使用哪個 Firebase 項目。

添加 Firebase 庫

要讓您的應用使用 Firebase,您需要將 Firebase 庫添加到應用中。有多種方法可以做到這一點,如Firebase 文檔中所述。例如,您可以從 Google 的 CDN 添加庫,或者您可以使用 npm 在本地安裝它們,然後在使用 Browserify 時將它們打包到您的應用程序中。

StackBlitz 提供自動捆綁,因此您可以使用 import 語句添加 Firebase 庫。您將使用庫的模塊化 (v9) 版本,這有助於通過稱為“搖樹”的過程減小網頁的整體大小。您可以在文檔中了解有關模塊化 SDK 的更多信息。

要構建此應用,您需要使用 Firebase Authentication、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 網絡應用添加到您的 Firebase 項目

  1. 返回 Firebase 控制台,點擊左上角的項目概覽,導航到項目的概覽​​頁面。
  2. 在項目概覽頁面的中心,單擊 Web 圖標網絡應用程序圖標創建一個新的 Firebase 網絡應用。

    此步驟的屏幕截圖

  3. 使用暱稱Web App註冊應用程序。
  4. 對於此 Codelab,請勿選中Also setup 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.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 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之後保存對 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.jsmain()函數底部添加以下代碼:
    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方法將消息內容添加到guestbook集合中的新文檔(具有自動生成的 ID)中。

  1. 在 StackBlitz 中,轉到index.js文件。
  2. 在頂部,找到 firebase 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 安全規則來保護您的數據庫。 (代碼實驗室後面有更多關於安全規則的信息。)

  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導入語句,然後添加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 控制台的Build部分,點擊Firestore Database ,然後選擇Rules標籤(或點擊此處直接轉到Rules標籤)。
  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變量是否不為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導入語句,然後添加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集合的偵聽器併計算YES響應的數量:

  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();
          // Subcribe 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 構建了一個交互式實時網絡應用程序!

我們涵蓋的內容

  • Firebase 身份驗證
  • Firebase 用戶界面
  • 雲防火牆
  • Firebase 安全規則

下一步

學到更多

進展如何?

我們希望得到您的反饋!請在此處填寫(非常)簡短的表格。