認識 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-container」和「description-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」>驗證:然後按一下「開始使用」。您現在進入「驗證」資訊主頁。您可以在這裡查看已登入的使用者、設定登入供應商及管理設定。

    這個步驟的螢幕截圖

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

    這個步驟的螢幕截圖

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

    這個步驟的螢幕截圖

設定 Cloud Firestore

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

以下說明如何設定 Cloud Firestore:

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

    這個步驟的螢幕截圖

  3. 選取「以測試模式啟動」選項。閱讀安全性規則免責事項。測試模式可確保您在開發期間能自由寫入資料庫。點選「下一步」

    這個步驟的螢幕截圖

  4. 選取資料庫位置 (您可以使用預設值)。不過請注意,這個地點在設定後即無法變更。

    這個步驟的螢幕截圖

  5. 按一下 [完成]

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. 在本程式碼研究室中,「請勿」勾選「一併為這個應用程式設定 Firebase 託管」旁的方塊。您將暫時使用 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. 新增使用者登入 (敬請回覆)

現在您已在應用程式中新增 Firebase,就可以設定 [回覆] 按鈕,使用 Firebase 驗證來註冊使用者。

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

您需要按一下「回覆」按鈕,提示使用者透過電子郵件地址登入。只要將 FirebaseUI 掛接至 RSVP 按鈕即可。FirebaseUI 是一種程式庫,可讓您在 Firebase 驗證的基礎上提供預先建構的 UI。

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

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

請新增程式碼來初始化 FirebaseUI 驗證

  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>
    
    <!-- ... -->
    

應用程式預覽

這個步驟的螢幕截圖

使用者按一下「傳送」按鈕,就會觸發下方的程式碼片段。這會將訊息輸入欄位的內容新增至資料庫的 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 Database 資訊主頁中,您應該會看到含有新訊息的 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 控制台的「資料庫」專區中,嘗試手動刪除、修改或新增訊息。任何變更應該都會顯示在使用者介面中

恭喜!您正在應用程式讀取 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. 按一下 [Publish] 即可部署新規則。現在,在留言板上,只有登入的使用者可以讀取訊息 (任何訊息!),但您只能使用自己的使用者 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 中,新增一組「YES」和「NO」按鈕,例如:
    <!-- ... -->
      <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 資訊主頁。

讀取回覆狀態

現在,您已經記錄了回應,接下來讓我們看看誰在 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() 函式的底部加入以下程式碼,即可監聽回覆狀態並計算 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 安全性規則

後續步驟

瞭解詳情

還好嗎?

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