1. 概要
この Codelab では、インタラクティブなウェブ アプリケーションを作成するために Firebase の基本を学びます。このチュートリアルでは、いくつかの Firebase プロダクトを使用して、イベントの出欠確認とゲストブックのチャットアプリを構築します。
学習内容
- Firebase Authentication と FirebaseUI を使用してユーザーを認証します。
- Cloud Firestore を使用してデータを同期する。
- Firebase セキュリティ ルールを作成してデータベースを保護する。
必要なもの
- Chrome などの任意のブラウザ。
- stackblitz.com へのアクセス(アカウントやログインは不要)。
- Google アカウント(Gmail アカウントなど)。GitHub アカウントですでにご利用のメール アカウントをおすすめします。これにより、StackBlitz の高度な機能を使用できます。
- この Codelab のサンプルコード。コードの取得方法については、次のステップをご覧ください。
2. 開始用コードを取得する
この Codelab では、いくつかの Firebase ワークフローが統合されたオンライン エディタである StackBlitz を使用してアプリを構築します。Stackblitz では、ソフトウェアのインストールや特別な StackBlitz アカウントは必要ありません。
StackBlitz では、プロジェクトを他のユーザーと共有できます。StackBlitz プロジェクトの URL を持っている他のユーザーは、コードを表示してプロジェクトをフォークできますが、StackBlitz プロジェクトを編集することはできません。
- 開始コードについては、https://stackblitz.com/edit/firebase-gtk-web-start にアクセスしてください。
- StackBlitz ページの上部にある [Fork] をクリックします。
これで、独自の StackBlitz プロジェクトとして開始コードのコピーが作成されました。このプロジェクトには、一意の名前と一意の URL が割り当てられています。すべてのファイルと変更はこの StackBlitz プロジェクトに保存されます。
3. イベント情報を編集する
この Codelab の開始用マテリアルには、ウェブアプリの構造がいくつか用意されています。これには、いくつかのスタイルシートと、アプリ用の 2 つの HTML コンテナが含まれます。この Codelab の後半で、これらのコンテナを Firebase に接続します。
まず、StackBlitz インターフェースについて詳しく見ていきましょう。
- StackBlitz で
index.html
ファイルを開きます。 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 プロジェクトを作成する
- Google アカウントを使用して Firebase コンソールにログインします。
- ボタンをクリックして新しいプロジェクトを作成し、プロジェクト名(例:
Firebase-Web-Codelab
)を入力します。 - [続行] をクリックします。
- Firebase の利用規約が表示されたら、内容を読み、同意して [続行] をクリックします。
- (省略可)Firebase コンソールで AI アシスタンス(「Gemini in Firebase」)を有効にします。
- この Codelab では Google アナリティクスは必要ないため、Google アナリティクスのオプションをオフに切り替えます。
- [プロジェクトを作成] をクリックし、プロジェクトのプロビジョニングが完了するまで待ってから、[続行] をクリックします。
Firebase プロジェクトの詳細については、Firebase プロジェクトについて理解するをご覧ください。
コンソールで Firebase プロダクトを有効にして設定する
これから構築するアプリでは、ウェブアプリに使用できるいくつかの Firebase プロダクトを使用します。
- ユーザーがアプリに簡単にログインできるようにするための Firebase Authentication と Firebase UI。
- 構造化されたデータをクラウドに保存し、データが変更されたときに即座に通知を受け取る Cloud Firestore。
- データベースを保護するための Firebase セキュリティ ルール。
この中には、特別な設定が必要になるプロダクトや、Firebase コンソールを使用して有効化する必要があるプロダクトがあります。
Firebase Authentication 用にメールアドレス ログインを有効にする
この Codelab では、ユーザーがウェブアプリにログインできるようにするために、メール/パスワード ログインを使用します。
- Firebase コンソールの左側のパネルで、[ビルド] > [Authentication] をクリックします。[開始する] をクリックします。認証ダッシュボードが表示されます。ここでは、登録ユーザーの確認、ログイン プロバイダの設定、設定の管理を行うことができます。
- [ログイン方法] タブを選択します(または、ここをクリックしてタブに直接移動します)。
- プロバイダ オプションから [メールアドレス/パスワード] をクリックし、スイッチを [有効] に切り替えて、[保存] をクリックします。
Cloud Firestore を設定する
このウェブアプリは Cloud Firestore を使用してチャット メッセージを保存し、新しいチャット メッセージを受信します。
Firebase プロジェクトで Cloud Firestore を設定する方法は次のとおりです。
- Firebase コンソールの左側のパネルで [ビルド] を展開し、[Firestore データベース] を選択します。
- [データベースを作成] をクリックします。
- [データベース ID] は
(default)
に設定したままにします。 - データベースの場所を選択し、[次へ] をクリックします。
実際のアプリでは、ユーザーに近い場所を選択します。 - [テストモードで開始] をクリックします。セキュリティ ルールに関する免責条項を確認します。
この Codelab の後半で、データを保護するためのセキュリティ ルールを追加します。データベースのセキュリティ ルールを追加せずに、アプリを配布または公開しないでください。 - [作成] をクリックします。
5. Firebase を追加して構成する
Firebase プロジェクトを作成し、いくつかのサービスを有効にしたら、Firebase を使用するコードと、使用する Firebase プロジェクトを指定する必要があります。
Firebase ライブラリを追加する
アプリで Firebase を使用するには、Firebase ライブラリをアプリに追加する必要があります。これを行う方法は複数あり、Firebase のドキュメントに記載されています。たとえば、Google の CDN からライブラリを追加できます。また、Browserify を使用している場合は、npm を使用してローカルにインストールしてから、アプリにパッケージ化することもできます。
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 ウェブアプリを追加する
- Firebase コンソールに戻り、左上の [プロジェクトの概要] をクリックして、プロジェクトの概要ページに移動します。
- プロジェクトの概要ページの中央にあるウェブアイコン
をクリックして、新しい Firebase ウェブアプリを作成します。
- アプリを「ウェブアプリ」というニックネームで登録します。
- この Codelab では、[このアプリの Firebase Hosting も設定します] の横にあるチェックボックスをオンにしないでください。ここでは StackBlitz のプレビュー ペインを使用します。
- [アプリの登録] をクリックします。
- Firebase 構成オブジェクトをクリップボードにコピーします。
- [コンソールに進む] をクリックします。Firebase 構成オブジェクトをアプリに追加します。
- StackBlitz に戻り、
index.js
ファイルに移動します。 Add Firebase project configuration object here
コメント行を見つけ、そのコメントのすぐ下に構成スニペットを貼り付けます。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 Authentication を使用してユーザーを登録する RSVP ボタンを設定できます。
メールログインと FirebaseUI を使用してユーザーを認証する
ユーザーにメールアドレスでのログインを求める RSVP ボタンが必要です。これを行うには、FirebaseUI を出欠確認ボタンにフックします。FirebaseUI は、Firebase Auth の上に構築された事前構築済みの UI を提供するライブラリです。
FirebaseUI には、次の 2 つを行う構成(ドキュメントのオプションを参照)が必要です。
- メール/パスワードのログイン方法を使用することを FirebaseUI に伝えます。
- ログインが成功した場合のコールバックを処理し、リダイレクトを回避するために false を返します。単一ページのウェブアプリを構築しているため、ページを更新したくありません。
FirebaseUI Auth を初期化するコードを追加する
- StackBlitz で
index.js
ファイルに移動します。 - 上部で
firebase/auth
import ステートメントを見つけ、次のようにgetAuth
とEmailAuthProvider
を追加します。// ... // Add the Firebase products and methods that you want to use import { getAuth, EmailAuthProvider } from 'firebase/auth'; import {} from 'firebase/firestore';
initializeApp
の直後に、次のように認証オブジェクトへの参照を保存します。initializeApp(firebaseConfig); auth = getAuth();
- FirebaseUI の構成は、開始コードですでに提供されています。メール認証プロバイダを使用するようにすでに設定されています。
index.js
のmain()
関数の下部に、次のように FirebaseUI の初期化ステートメントを追加します。async function main() { // ... // Initialize the FirebaseUI widget using Firebase const ui = new firebaseui.auth.AuthUI(auth); } main();
HTML に出欠確認ボタンを追加する
- StackBlitz で
index.html
ファイルに移動します。 - 次の例に示すように、
event-details-container
内に RSVP ボタンの HTML を追加します。
この Codelab では、index.js
ファイルにこれらの特定の ID のフックがすでに存在するため、以下に示すように同じ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> <!-- ... -->
- [RSVP] ボタンにリスナーを設定し、FirebaseUI の開始関数を呼び出します。これにより、ログイン ウィンドウを表示するように FirebaseUI に指示します。
index.js
のmain()
関数の末尾に次のコードを追加します。async function main() { // ... // Listen to RSVP button clicks startRsvpButton.addEventListener("click", () => { ui.start("#firebaseui-auth-container", uiConfig); }); } main();
アプリへのログインをテストする
- StackBlitz のプレビュー ウィンドウで、[RSVP] ボタンをクリックしてアプリにログインします。
- この Codelab ではメール確認の手順を設定しないため、任意のメールアドレス(偽のメールアドレスでも可)を使用できます。
auth/operation-not-allowed
またはThe given sign-in provider is disabled for this Firebase project
というエラー メッセージが表示された場合は、Firebase コンソールでログイン プロバイダとしてメール/パスワードを有効にしていることを確認してください。
- Firebase コンソールの認証ダッシュボードに移動します。[ユーザー] タブに、アプリへのログインに使用したアカウント情報が表示されます。
認証状態を UI に追加する
次に、ログインしていることが UI に反映されていることを確認します。
Firebase Authentication 状態リスナー コールバックを使用します。このコールバックは、ユーザーのログイン ステータスが変更されるたびに通知を受け取ります。現在ログインしているユーザーがいる場合、アプリは [出欠確認] ボタンを [ログアウト] ボタンに切り替えます。
- StackBlitz で
index.js
ファイルに移動します。 - 上部で
firebase/auth
import ステートメントを見つけ、次のようにsignOut
とonAuthStateChanged
を追加します。// ... // Add the Firebase products and methods that you want to use import { getAuth, EmailAuthProvider, signOut, onAuthStateChanged } from 'firebase/auth'; import {} from 'firebase/firestore';
- 次のコードを
main()
関数の末尾に追加します。async function main() { // ... // Listen to the current Auth state onAuthStateChanged(auth, user => { if (user) { startRsvpButton.textContent = 'LOGOUT'; } else { startRsvpButton.textContent = 'RSVP'; } }); } main();
- ボタン リスナーで、現在のユーザーがいるかどうかを確認し、ログアウトします。これを行うには、現在の
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 に追加する
このセクションでは、ユーザーがデータベースに新しいメッセージを書き込めるようにする機能を追加します。まず、UI 要素(メッセージ フィールドと送信ボタン)の HTML を追加します。次に、これらの要素をデータベースに接続するコードを追加します。
メッセージ フィールドと送信ボタンの UI 要素を追加するには:
- StackBlitz で
index.html
ファイルに移動します。 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 を持つ)に追加します。
- StackBlitz で
index.js
ファイルに移動します。 - 上部にある
firebase/firestore
import ステートメントを見つけて、次のようにgetFirestore
、addDoc
、collection
を追加します。// ... // 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';
- 次に、
initializeApp
の直後に Firestoredb
オブジェクトへの参照を保存します。initializeApp(firebaseConfig); auth = getAuth(); db = getFirestore();
main()
関数の下部に次のコードを追加します。
なお、auth.currentUser.uid
は、Firebase Authentication がすべてのログイン済みユーザーに付与する自動生成の一意の 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 の後半で説明します)。
- StackBlitz で
index.js
ファイルに移動します。 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'; } });
メッセージの送信をテストする
- アプリにログインしていることを確認します。
- 「Hey there!」などのメッセージを入力し、[送信] をクリックします。
このアクションは、メッセージを Cloud Firestore データベースに書き込みます。ただし、データの取得が実装されていないため、実際のウェブアプリにはまだメッセージが表示されません。これは次の手順で行います。
ただし、Firebase コンソールで新しく追加されたメッセージを確認できます。
Firebase コンソールの Firestore Database ダッシュボードに、新しく追加されたメッセージを含む guestbook
コレクションが表示されます。メッセージを送信し続けると、ゲストブック コレクションには次のようなドキュメントが多数含まれるようになります。
Firebase コンソール
8. メッセージを読む
メッセージを同期する
ゲストがデータベースにメッセージを書き込めるのは素晴らしいことですが、まだアプリでメッセージを表示することはできません。
メッセージを表示するには、データが変更されたときにトリガーするリスナーを追加し、新しいメッセージを表示する UI 要素を作成する必要があります。
アプリから新しく追加されたメッセージをリッスンするコードを追加します。まず、メッセージを表示するセクションを HTML に追加します。
- StackBlitz で
index.html
ファイルに移動します。 guestbook-container
に、ID がguestbook
の新しいセクションを追加します。<!-- ... --> <section id="guestbook-container"> <h2>Discussion</h2> <form><!-- ... --></form> <section id="guestbook"></section> </section> <!-- ... -->
次に、データに対する変更をリッスンするリスナーを登録します。
- StackBlitz で
index.js
ファイルに移動します。 - 上部にある
firebase/firestore
import ステートメントを見つけて、次のようにquery
、orderBy
、onSnapshot
を追加します。// ... import { getFirestore, addDoc, collection, query, orderBy, onSnapshot } from 'firebase/firestore';
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
関数は、使用するクエリとコールバック関数の 2 つのパラメータを取ります。クエリに一致するドキュメントに変更があると、コールバック関数がトリガーされます。これは、メッセージが削除、変更、追加された場合のいずれかです。詳細については、Cloud Firestore のドキュメントをご覧ください。
メッセージの同期をテストする
Cloud Firestore は、データベースに登録されているクライアントとデータを自動的かつ瞬時に同期します。
- 先ほどデータベースで作成したメッセージがアプリに表示されます。新しいメッセージを書き込むと、すぐに表示されます。
- ワークスペースを複数のウィンドウまたはタブで開いている場合、メッセージはタブ間でリアルタイムに同期されます。
- (省略可)Firebase コンソールの [Database] で直接、メッセージを手動で削除、変更したり新しいメッセージを追加したりしてみてください。これらの変更も UI に反映されるはずです。
これで完了です。アプリで Cloud Firestore ドキュメントを読み取っています。
アプリのプレビュー
9. 基本的なセキュリティ ルールを設定する
Cloud Firestore をテストモードで使用するように初期設定したため、データベースは読み取りと書き込みが可能な状態になっています。ただし、テストモードは開発の初期段階でのみ使用してください。ベスト プラクティスとして、アプリの開発時にデータベースのセキュリティ ルールを設定する必要があります。セキュリティは、アプリの構造と動作に不可欠な要素です。
セキュリティ ルールを使用すると、データベース内のドキュメントとコレクションへのアクセスを制御できます。ルールの構文の柔軟性により、データベース全体へのすべての書き込みから特定のドキュメントに対するオペレーションまで、どのようなものにも一致するルールを作成できます。
Firebase コンソールで Cloud Firestore のセキュリティ ルールを記述できます。
- Firebase コンソールの [構築] セクションで、[Firestore データベース] をクリックし、[ルール] タブを選択します(または、こちらをクリックして [ルール] タブに直接移動します)。
- 次のデフォルトのセキュリティ ルールが表示されます。公開アクセス期間は、今日から数週間です。
コレクションを特定する
まず、アプリがデータを書き込むコレクションを特定します。
- 既存の
match /{document=**}
条項を削除して、ルールを次のようにします。rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { } }
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. } }
セキュリティ ルールを追加する
各ゲストブック ドキュメントのフィールドとして Authentication UID を使用したため、Authentication UID を取得して、ドキュメントへの書き込みを試みるユーザーが一致する Authentication UID を持っていることを確認できます。
- 以下に示すように、読み取りルールと書き込みルールをルールセットに追加します。
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; } } }
- [公開] をクリックして、新しいルールをデプロイします。これで、ゲストブックではログインしたユーザーのみがメッセージ(すべてのメッセージ)を読めるようになりますが、メッセージを作成できるのはユーザー ID を使用した場合のみとなります。また、メッセージの編集や削除も許可されていません。
検証ルールを追加する
- データ検証を追加して、想定されるすべてのフィールドがドキュメントに存在することを確認します。
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; } } }
- [公開] をクリックして、新しいルールをデプロイします。
リスナーをリセット
アプリで認証されたユーザーのみがログインできるようになったため、ゲストブックの firestore
クエリを認証リスナー内に移動する必要があります。そうしないと、権限エラーが発生し、ユーザーがログアウトしたときにアプリが切断されます。
- StackBlitz で
index.js
ファイルに移動します。 - ゲストブック コレクション
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); }); }); }
- その下に
unsubscribeGuestbook
という新しい関数を追加します。guestbookListener
変数が null でないかどうかを確認し、リスナーをキャンセルする関数を呼び出します。// ... // Unsubscribe from guestbook updates function unsubscribeGuestbook() { if (guestbookListener != null) { guestbookListener(); guestbookListener = null; } }
最後に、新しい関数を onAuthStateChanged
コールバックに追加します。
if (user)
の末尾にsubscribeGuestbook()
を追加します。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. ボーナス ステップ: 学習した内容を実践する
参加者の出欠確認ステータスを記録する
現在のアプリでは、イベントに関心のあるユーザーがチャットを開始できるだけです。また、他のユーザーが参加しようとしているかどうかを知る唯一の方法は、チャットに投稿してもらうことです。参加人数を把握して、参加者に知らせましょう。
イベントに参加したいユーザーを登録するための切り替えを追加し、参加者の数を集計します。
- StackBlitz で
index.html
ファイルに移動します。 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 を使用してレスポンスをデータベースに保存します。
- StackBlitz で
index.js
ファイルに移動します。 - 上部にある
firebase/firestore
import ステートメントを見つけて、次のようにdoc
、setDoc
、where
を追加します。// ... // Add the Firebase products and methods that you want to use import { getFirestore, addDoc, collection, query, orderBy, onSnapshot, doc, setDoc, where } from 'firebase/firestore';
main()
関数の下部に、次のコードを追加して、出欠確認のステータスをリッスンします。async function main() { // ... // Listen to RSVP responses rsvpYes.onclick = async () => { }; rsvpNo.onclick = async () => { }; } main();
- 次に、
attendees
という新しいコレクションを作成し、出欠確認ボタンのいずれかがクリックされた場合にドキュメント参照を登録します。クリックされたボタンに応じて、その参照をtrue
またはfalse
に設定します。
まず、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
コレクションへの追加を許可するようにルールを更新する必要があります。
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; } } }
- [公開] をクリックして、新しいルールをデプロイします。
検証ルールを追加する
- データ検証ルールを追加して、想定されるすべてのフィールドがドキュメントに存在することを確認します。
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; } } }
- ルールをデプロイするには、[公開] をクリックしてください。
(省略可)ボタンをクリックした結果が表示されます。Firebase コンソールの Cloud Firestore ダッシュボードに移動します。
出欠確認のステータスを読み取る
回答を記録したので、参加者を確認して UI に反映しましょう。
- StackBlitz で
index.html
ファイルに移動します。 description-container
で、ID がnumber-attending
の新しい要素を追加します。<!-- ... --> <section id="description-container"> <!-- ... --> <p id="number-attending"></p> </section> <!-- ... -->
次に、attendees
コレクションのリスナーを登録し、YES レスポンスの数をカウントします。
- StackBlitz で
index.js
ファイルに移動します。 main()
関数の下部に次のコードを追加して、出欠確認のステータスをリッスンし、[はい] をクリックした回数をカウントします。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();
最後に、現在のステータスに対応するボタンをハイライト表示します。
- 現在の認証 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'; } } }); }
- また、登録解除用の関数も作成しましょう。これは、ユーザーがログアウトするときに使用されます。
// ... function unsubscribeCurrentRSVP() { if (rsvpListener != null) { rsvpListener(); rsvpListener = null; } rsvpYes.className = ''; rsvpNo.className = ''; }
- 認証リスナーから関数を呼び出します。
// ... // 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(); } });
- 複数のユーザーとしてログインし、[YES] ボタンをクリックするたびにカウントが増加することを確認します。
アプリのプレビュー
11. 完了
Firebase を使用して、インタラクティブなリアルタイム ウェブ アプリケーションを構築しました。
学習した内容
- Firebase Authentication
- FirebaseUI
- Cloud Firestore
- Firebase セキュリティ ルール
次のステップ
- Firebase デベロッパー ワークフローの詳細Firebase エミュレータ Codelab をご覧になり、アプリをローカルで徹底的にテストして実行する方法について学習してください。
- 他の Firebase プロダクトの詳細ユーザーがアップロードした画像ファイルを保存したい場合もあるでしょう。または、ユーザーに通知を送信しますか?ウェブ向けの Firebase プロダクトの詳細については、Firebase ウェブ Codelab をご覧ください。
- Cloud Firestore についてご関心をお持ちの方は、サブコレクションとトランザクションについて学習したいですか?Cloud Firestore についてさらに詳しく説明するコードラボについては、Cloud Firestore ウェブ コードラボをご覧ください。または、Cloud Firestore について学ぶための YouTube シリーズをご覧ください。
詳細
- Firebase サイト: firebase.google.com
- Firebase の YouTube チャンネル
いかがでしたか?
皆様からのご意見をお待ちしております。こちらから簡単なフォームにご記入ください。