Codelab กรอบงานเว็บเชิงมุมของ Firebase

1. สิ่งที่คุณจะสร้าง

ใน Codelab นี้ คุณจะสร้างบล็อกการเดินทางด้วยแผนที่การทำงานร่วมกันแบบเรียลไทม์พร้อมแผนที่ล่าสุดจากไลบรารี Angular ของเรา: AngularFire เว็บแอปสุดท้ายจะประกอบด้วยบล็อกการเดินทางซึ่งคุณสามารถอัปโหลดรูปภาพไปยังสถานที่แต่ละแห่งที่คุณเคยเดินทางไปได้

AngularFire จะถูกใช้เพื่อสร้างเว็บแอป, Emulator Suite สำหรับการทดสอบในพื้นที่, การตรวจสอบสิทธิ์เพื่อติดตามข้อมูลผู้ใช้, Firestore และ Storage เพื่อคงข้อมูลและสื่อ ขับเคลื่อนโดย Cloud Functions และสุดท้ายคือ Firebase Hosting เพื่อปรับใช้แอป

สิ่งที่คุณจะได้เรียนรู้

  • วิธีการพัฒนาด้วยผลิตภัณฑ์ Firebase ภายในเครื่องด้วย Emulator Suite
  • วิธีปรับปรุงเว็บแอปของคุณด้วย AngularFire
  • วิธีเก็บข้อมูลของคุณใน Firestore
  • วิธีคงสื่อไว้ในพื้นที่จัดเก็บข้อมูล
  • วิธีปรับใช้แอปของคุณกับ Firebase Hosting
  • วิธีใช้ Cloud Functions เพื่อโต้ตอบกับฐานข้อมูลและ API ของคุณ

สิ่งที่คุณต้องการ

  • Node.js เวอร์ชัน 10 ขึ้นไป
  • บัญชี Google สำหรับการสร้างและการจัดการโครงการ Firebase ของคุณ
  • Firebase CLI เวอร์ชัน 11.14.2 หรือใหม่กว่า
  • เบราว์เซอร์ที่คุณเลือก เช่น Chrome
  • ความเข้าใจพื้นฐานเกี่ยวกับ Angular และ Javascript

2. รับโค้ดตัวอย่าง

โคลน ที่เก็บ GitHub ของ codelab จากบรรทัดคำสั่ง:

git clone https://github.com/firebase/codelab-friendlychat-web

หรือหากคุณไม่ได้ติดตั้ง git คุณสามารถ ดาวน์โหลดที่เก็บเป็นไฟล์ ZIP ได้

พื้นที่เก็บข้อมูล Github มีโปรเจ็กต์ตัวอย่างสำหรับหลายแพลตฟอร์ม

Codelab นี้ใช้เฉพาะพื้นที่เก็บข้อมูล webframework เท่านั้น:

  • 📁 webframework : โค้ดเริ่มต้นที่คุณจะสร้างขึ้นในระหว่าง Codelab นี้

ติดตั้งการพึ่งพา

หลังจากการโคลน ให้ติดตั้งการขึ้นต่อกันในโฟลเดอร์รูทและ functions ก่อนสร้างเว็บแอป

cd webframework && npm install
cd functions && npm install

ติดตั้ง Firebase CLI

ติดตั้ง Firebase CLI โดยใช้คำสั่งนี้ในเทอร์มินัล:

npm install -g firebase-tools

ตรวจสอบอีกครั้งว่าเวอร์ชัน Firebase CLI ของคุณมากกว่า 11.14.2 โดยใช้:

firebase  --version

หากเวอร์ชันของคุณต่ำกว่า 11.14.2 โปรดอัปเดตโดยใช้:

npm update firebase-tools

3. สร้างและตั้งค่าโปรเจ็กต์ Firebase

สร้างโปรเจ็กต์ Firebase

  1. ลงชื่อเข้าใช้ Firebase
  2. ในคอนโซล Firebase คลิก เพิ่มโครงการ จากนั้นตั้งชื่อโครงการ Firebase ของคุณ <your-project> จำรหัสโปรเจ็กต์สำหรับโปรเจ็กต์ Firebase ของคุณ
  3. คลิก สร้างโครงการ

สิ่งสำคัญ : โปรเจ็กต์ Firebase ของคุณจะมีชื่อว่า <your-project> แต่ Firebase จะกำหนดรหัสโปรเจ็กต์ที่ไม่ซ้ำให้กับโปรเจ็กต์โดยอัตโนมัติในรูปแบบ <your-project>-1234 ตัวระบุที่ไม่ซ้ำกันนี้คือวิธีการระบุโปรเจ็กต์ของคุณอย่างแท้จริง (รวมถึงใน CLI) ในขณะที่ <your-project> เป็นเพียงชื่อที่แสดง

แอปพลิเคชันที่เราจะสร้างใช้ผลิตภัณฑ์ Firebase ที่พร้อมใช้งานสำหรับเว็บแอป:

  • การตรวจสอบสิทธิ์ Firebase เพื่อให้ผู้ใช้ลงชื่อเข้าใช้แอปของคุณได้อย่างง่ายดาย
  • Cloud Firestore เพื่อบันทึกข้อมูลที่มีโครงสร้างบนคลาวด์และรับการแจ้งเตือนทันทีเมื่อข้อมูลมีการเปลี่ยนแปลง
  • Cloud Storage สำหรับ Firebase เพื่อบันทึกไฟล์ในระบบคลาวด์
  • Firebase Hosting เพื่อโฮสต์และให้บริการทรัพย์สินของคุณ
  • ฟังก์ชั่น โต้ตอบกับ API ภายในและภายนอก

ผลิตภัณฑ์เหล่านี้บางส่วนจำเป็นต้องมีการกำหนดค่าพิเศษหรือจำเป็นต้องเปิดใช้งานโดยใช้คอนโซล Firebase

เพิ่มเว็บแอป Firebase ให้กับโปรเจ็กต์

  1. คลิกไอคอนเว็บเพื่อสร้างเว็บแอป Firebase ใหม่
  2. ในขั้นตอนถัดไป คุณจะเห็นออบเจ็กต์การกำหนดค่า คัดลอกเนื้อหาของออบเจ็กต์นี้ลงในไฟล์ environments/environment.ts

เปิดใช้งาน การลงชื่อเข้าใช้ Google สำหรับการตรวจสอบสิทธิ์ Firebase

หากต้องการอนุญาตให้ผู้ใช้ลงชื่อเข้าใช้เว็บแอปด้วยบัญชี Google เราจะใช้วิธีการลงชื่อเข้าใช้ Google

หากต้องการเปิดใช้งานการลงชื่อเข้าใช้ Google :

  1. ในคอนโซล Firebase ให้ค้นหาส่วน Build ในแผงด้านซ้าย
  2. คลิก การรับรองความถูกต้อง จากนั้นคลิกแท็บ วิธีการลงชื่อเข้าใช้ (หรือ คลิกที่นี่ เพื่อไปยังที่นั่นโดยตรง)
  3. เปิดใช้งานผู้ให้บริการลงชื่อเข้าใช้ Google จากนั้นคลิก บันทึก
  4. ตั้งชื่อที่เปิดเผยต่อสาธารณะของแอปของคุณเป็น <ชื่อ โครงการของคุณ> และเลือก อีเมลสนับสนุนโครงการ จากเมนูแบบเลื่อนลง

เปิดใช้งาน Cloud Firestore

  1. ในส่วน Build ของคอนโซล Firebase ให้คลิก Firestore Database
  2. คลิก สร้างฐานข้อมูล ในบานหน้าต่าง Cloud Firestore
  3. กำหนดตำแหน่งที่จัดเก็บข้อมูล Cloud Firestore ของคุณ คุณสามารถปล่อยให้สิ่งนี้เป็นค่าเริ่มต้นหรือเลือกภูมิภาคใกล้กับคุณ

เปิดใช้งานที่เก็บข้อมูลบนคลาวด์

เว็บแอปใช้ Cloud Storage สำหรับ Firebase เพื่อจัดเก็บ อัปโหลด และแชร์รูปภาพ

  1. ในส่วน Build ของคอนโซล Firebase ให้คลิก Storage
  2. หากไม่มีปุ่ม เริ่มต้น ใช้งาน แสดงว่ามีการจัดเก็บข้อมูลบนคลาวด์แล้ว

เปิดใช้งานแล้ว และคุณไม่จำเป็นต้องทำตามขั้นตอนด้านล่าง

  1. คลิก เริ่มต้นใช้งาน
  2. อ่านข้อจำกัดความรับผิดชอบเกี่ยวกับกฎความปลอดภัยสำหรับโปรเจ็กต์ Firebase ของคุณ จากนั้นคลิก ถัดไป
  3. ตำแหน่ง Cloud Storage ได้รับการเลือกไว้ล่วงหน้าด้วยภูมิภาคเดียวกับที่คุณเลือกสำหรับฐานข้อมูล Cloud Firestore ของคุณ คลิก เสร็จสิ้น เพื่อสิ้นสุดการตั้งค่า

เมื่อใช้กฎความปลอดภัยเริ่มต้น ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์จะเขียนอะไรก็ได้ลงใน Cloud Storage เราจะทำให้พื้นที่เก็บข้อมูลของเราปลอดภัยยิ่งขึ้นในภายหลังใน Codelab นี้

4. เชื่อมต่อกับโปรเจ็กต์ Firebase ของคุณ

อินเทอร์เฟซบรรทัดคำสั่ง (CLI) ของ Firebase ช่วยให้คุณใช้โฮสติ้งของ Firebase เพื่อให้บริการเว็บแอปของคุณภายในเครื่อง รวมถึงปรับใช้เว็บแอปกับโปรเจ็กต์ Firebase ของคุณ

ตรวจสอบให้แน่ใจว่าบรรทัดคำสั่งของคุณเข้าถึงไดเร็กทอรี webframework ในเครื่องของแอป

เชื่อมต่อโค้ดเว็บแอปกับโปรเจ็กต์ Firebase ของคุณ ขั้นแรก ให้เข้าสู่ระบบ Firebase CLI ในบรรทัดคำสั่ง:

firebase login

จากนั้นรันคำสั่งต่อไปนี้เพื่อสร้างนามแฝงโครงการ แทนที่ $YOUR_PROJECT_ID ด้วย ID ของโครงการ Firebase ของคุณ

firebase  use  $YOUR_PROJECT_ID

เพิ่มแองกูลาร์ไฟร์

หากต้องการเพิ่ม AngularFire ให้กับแอป ให้รันคำสั่ง:

ng add @angular/fire

จากนั้น ทำตามคำแนะนำบรรทัดคำสั่ง และเลือกฟีเจอร์ที่มีอยู่ในโปรเจ็กต์ Firebase ของคุณ

เริ่มต้น Firebase

ในการเริ่มต้นโครงการ Firebase ให้รัน:

firebase init

จากนั้น ปฏิบัติตามข้อความแจ้งของบรรทัดคำสั่ง ให้เลือกฟีเจอร์และโปรแกรมจำลองที่ใช้ในโปรเจ็กต์ Firebase ของคุณ

เริ่มโปรแกรมจำลอง

จากไดเร็กทอรี webframework ให้รันคำสั่งต่อไปนี้เพื่อเริ่มอีมูเลเตอร์:

firebase  emulators:start

ในที่สุดคุณควรเห็นสิ่งนี้:

$  firebase  emulators:start

i  emulators:  Starting  emulators:  auth,  functions,  firestore,  hosting,  functions

i  firestore:  Firestore  Emulator  logging  to  firestore-debug.log

i  hosting:  Serving  hosting  files  from:  public

✔  hosting:  Local  server:  http://localhost:5000

i  ui:  Emulator  UI  logging  to  ui-debug.log

i  functions:  Watching  "/functions"  for  Cloud  Functions...

✔  functions[updateMap]:  firestore  function  initialized.

  

┌─────────────────────────────────────────────────────────────┐

│  ✔  All  emulators  ready!  It  is  now  safe  to  connect  your  app.  │

│  i  View  Emulator  UI  at  http://localhost:4000  │

└─────────────────────────────────────────────────────────────┘

  

┌────────────────┬────────────────┬─────────────────────────────────┐

│  Emulator  │  Host:Port  │  View  in  Emulator  UI  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Authentication  │  localhost:9099  │  http://localhost:4000/auth  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Functions  │  localhost:5001  │  http://localhost:4000/functions  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Firestore  │  localhost:8080  │  http://localhost:4000/firestore  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Hosting  │  localhost:5000  │  n/a  │

└────────────────┴────────────────┴─────────────────────────────────┘

Emulator  Hub  running  at  localhost:4400

Other  reserved  ports:  4500

  

Issues?  Report  them  at  https://github.com/firebase/firebase-tools/issues  and  attach  the  *-debug.log  files.

เมื่อคุณเห็น ✔All emulators ready! ข้อความแสดงว่าโปรแกรมจำลองพร้อมใช้งานแล้ว

คุณควรเห็น UI ของแอปการเดินทางของคุณซึ่ง (ยัง) ไม่ทำงาน:

ตอนนี้มาสร้างกันเถอะ!

5. เชื่อมต่อเว็บแอปกับโปรแกรมจำลอง

ขึ้นอยู่กับตารางในบันทึกของโปรแกรมจำลอง โปรแกรมจำลอง Cloud Firestore กำลังฟังบนพอร์ต 8080 และโปรแกรมจำลองการตรวจสอบสิทธิ์กำลังฟังบนพอร์ต 9099

เปิด EmulatorUI

ในเว็บเบราว์เซอร์ของคุณ ให้ไปที่ http://127.0.0.1:4000/ คุณควรเห็น Emulator Suite UI

กำหนดเส้นทางแอปเพื่อใช้โปรแกรมจำลอง

ใน src/app/app.module.ts ให้เพิ่มโค้ดต่อไปนี้ลงในรายการนำเข้าของ AppModule :

@NgModule({
	declarations: [...],
	imports: [
		provideFirebaseApp(() =>  initializeApp(environment.firebase)),

		provideAuth(() => {
			const  auth = getAuth();
			if (location.hostname === 'localhost') {
				connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings:  true });
			}
			return  auth;
		}),

		provideFirestore(() => {
			const  firestore = getFirestore();
			if (location.hostname === 'localhost') {
				connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
			}
			return  firestore;
		}),

		provideFunctions(() => {
			const  functions = getFunctions();
			if (location.hostname === 'localhost') {
				connectFunctionsEmulator(functions, '127.0.0.1', 5001);
			}
			return  functions;
		}),

		provideStorage(() => {
			const  storage = getStorage();
			if (location.hostname === 'localhost') {
				connectStorageEmulator(storage, '127.0.0.1', 5001);
			}
			return  storage;
		}),
		...
	]

ขณะนี้แอปได้รับการกำหนดค่าให้ใช้โปรแกรมจำลองในเครื่อง ทำให้สามารถทำการทดสอบและการพัฒนาในเครื่องได้

6. การเพิ่มการรับรองความถูกต้อง

เมื่อตั้งค่าโปรแกรมจำลองสำหรับแอปแล้ว เราก็สามารถเพิ่มคุณสมบัติการตรวจสอบสิทธิ์เพื่อให้แน่ใจว่าผู้ใช้แต่ละรายลงชื่อเข้าใช้ก่อนที่จะโพสต์ข้อความ

ในการทำเช่นนั้น เราสามารถนำเข้าฟังก์ชัน signin โดยตรงจาก AngularFire และติดตามสถานะการรับรองความถูกต้องของผู้ใช้ของคุณด้วยฟังก์ชัน authState แก้ไขฟังก์ชันหน้าเข้าสู่ระบบเพื่อให้หน้าตรวจสอบสถานะการรับรองความถูกต้องของผู้ใช้ในขณะโหลด

การฉีด AngularFire Auth

ใน src/app/pages/login-page/login-page.component.ts ให้นำเข้า Auth จาก @angular/fire/auth และแทรกลงใน LoginPageComponent ผู้ให้บริการการตรวจสอบสิทธิ์ เช่น Google และฟังก์ชันต่างๆ เช่น การ signin signout การออกจากระบบ สามารถนำเข้าได้โดยตรงจากแพ็คเกจเดียวกันและใช้ในแอป

import { Auth, GoogleAuthProvider, signInWithPopup, signOut, user } from  '@angular/fire/auth';

export  class  LoginPageComponent  implements  OnInit {
	private  auth: Auth = inject(Auth);
	private  provider = new  GoogleAuthProvider();
	user$ = user(this.auth);
	constructor() {}  

	ngOnInit(): void {} 

	login() {
		signInWithPopup(this.auth, this.provider).then((result) => {
			const  credential = GoogleAuthProvider.credentialFromResult(result);
			return  credential;
		})
	}

	logout() {
		signOut(this.auth).then(() => {
			console.log('signed out');}).catch((error) => {
				console.log('sign out error: ' + error);
		})
	}
}

ตอนนี้หน้าเข้าสู่ระบบใช้งานได้แล้ว! ลองเข้าสู่ระบบและดูผลลัพธ์ใน Authentication Emulator

7. การกำหนดค่า Firestore

ในขั้นตอนนี้ คุณจะเพิ่มฟังก์ชันในการโพสต์และอัปเดตโพสต์บล็อกการเดินทางที่จัดเก็บไว้ใน Firestore

เช่นเดียวกับการรับรองความถูกต้อง ฟังก์ชัน Firestore ได้รับการบรรจุล่วงหน้าจาก AngularFire เอกสารแต่ละฉบับเป็นของคอลเลกชัน และแต่ละเอกสารยังสามารถมีคอลเลกชันที่ซ้อนกันได้ จำเป็นต้องทราบ path ของเอกสารใน Firestore เพื่อสร้างและอัปเดตโพสต์บล็อกการเดินทาง

การนำบริการท่องเที่ยวไปใช้

เนื่องจากเพจต่างๆ จำนวนมากจำเป็นต้องอ่านและอัปเดตเอกสาร Firestore ในเว็บแอป เราจึงสามารถใช้ฟังก์ชันใน src/app/services/travel.service.ts ได้ เพื่อหลีกเลี่ยงการแทรกฟังก์ชัน AngularFire เดียวกันซ้ำๆ ทุกๆ หน้า

เริ่มต้นด้วยการแทรก Auth ซึ่งคล้ายกับขั้นตอนก่อนหน้า รวมถึง Firestore ลงในบริการของเรา การกำหนดวัตถุ user$ ที่สังเกตได้ซึ่งรับฟังสถานะการรับรองความถูกต้องปัจจุบันก็มีประโยชน์เช่นกัน

import { doc, docData, DocumentReference, Firestore, getDoc, setDoc, updateDoc, collection, addDoc, deleteDoc, collectionData, Timestamp } from  "@angular/fire/firestore";

export  class  TravelService {
	firestore: Firestore = inject(Firestore);
	auth: Auth = inject(Auth);
	user$ = authState(this.auth).pipe(filter(user  =>  user !== null), map(user  =>  user!));
	router: Router = inject(Router);

การเพิ่มโพสต์ท่องเที่ยว

โพสต์การเดินทางจะมีอยู่เป็นเอกสารที่จัดเก็บไว้ใน Firestore และเนื่องจากเอกสารต้องมีอยู่ในคอลเลกชัน คอลเลกชันที่มีโพสต์การเดินทางทั้งหมดจึงมีชื่อว่า travels ดังนั้นเส้นทางของจุดท่องเที่ยวใด ๆ จะเป็นการ travels/

การใช้ฟังก์ชัน addDoc จาก AngularFire จะทำให้วัตถุสามารถแทรกลงในคอลเลกชันได้:

async  addEmptyTravel(userId: String) {
	...
	addDoc(collection(this.firestore, 'travels'), travelData).then((travelRef) => {
		collection(this.firestore, `travels/${travelRef.id}/stops`);
		setDoc(travelRef, {... travelData, id:  travelRef.id})
		this.router.navigate(['edit', `${travelRef.id}`]);
		return  travelRef;

	})
}

การอัปเดตและการลบข้อมูล

ด้วย uid ของโพสต์การเดินทางใดๆ เราสามารถอนุมานเส้นทางของเอกสารที่จัดเก็บไว้ใน Firestore ซึ่งสามารถอ่าน อัปเดต หรือลบได้โดยใช้ฟังก์ชัน updateFoc และ deleteDoc ของ AngularFire:

async  updateData(path: string, data: Partial<Travel | Stop>) {
	await  updateDoc(doc(this.firestore, path), data)
}

async  deleteData(path: string) {
	const  ref = doc(this.firestore, path);
	await  deleteDoc(ref)
}

การอ่านข้อมูลเป็นแบบสังเกตได้

เนื่องจากโพสต์การเดินทางและจุดแวะระหว่างทางสามารถแก้ไขได้หลังการสร้าง จึงจะมีประโยชน์มากกว่าหากได้รับออบเจ็กต์เอกสารที่สังเกตได้ เพื่อสมัครรับการเปลี่ยนแปลงใดๆ ที่เกิดขึ้น ฟังก์ชันนี้นำเสนอโดยฟังก์ชัน docData และ collectionData จาก @angular/fire/firestore

getDocData(path: string) {
	return  docData(doc(this.firestore, path), {idField:  'id'}) as  Observable<Travel | Stop>
}

  
getCollectionData(path: string) {
	return  collectionData(collection(this.firestore, path), {idField:  'id'}) as  Observable<Travel[] | Stop[]>
}

การเพิ่มจุดแวะลงในโพสต์ท่องเที่ยว

เมื่อตั้งค่าการดำเนินการโพสต์การเดินทางแล้ว ก็ถึงเวลาพิจารณาจุดแวะพัก ซึ่งจะอยู่ภายใต้คอลเลกชันย่อยของโพสต์การเดินทาง เช่น: travels/ /stops/ travels/ /stops/

สิ่งนี้แทบจะเหมือนกับการสร้างโพสต์เกี่ยวกับการท่องเที่ยว ดังนั้นลองท้าทายตัวเองให้นำไปใช้ด้วยตัวเอง หรือลองดูการใช้งานด้านล่าง:

async  addStop(travelId: string) {
	...
	const  ref = await  addDoc(collection(this.firestore, `travels/${travelId}/stops`), stopData)
	setDoc(ref, {...stopData, id:  ref.id})
}

ดี! ฟังก์ชัน Firestore ได้ถูกนำไปใช้ในบริการการเดินทาง ดังนั้น ตอนนี้คุณสามารถเห็นการทำงานเหล่านั้นได้

การใช้ฟังก์ชัน Firestore ในแอป

ไปที่ src/app/pages/my-travels/my-travels.component.ts และแทรก TravelService เพื่อใช้ฟังก์ชันต่างๆ

travelService = inject(TravelService);
travelsData$: Observable<Travel[]>;
stopsList$!: Observable<Stop[]>;
constructor() {
	this.travelsData$ = this.travelService.getCollectionData(`travels`) as  Observable<Travel[]>
}

TravelService ถูกเรียกในตัวสร้างเพื่อรับอาร์เรย์ที่สังเกตได้ของการเดินทางทั้งหมด

ในกรณีที่ต้องการเฉพาะการเดินทางของผู้ใช้ปัจจุบัน ให้ใช้ ฟังก์ชัน query

วิธีอื่นๆ ในการรับรองความปลอดภัย ได้แก่ การใช้กฎความปลอดภัย หรือใช้ฟังก์ชันคลาวด์กับ Firestore ตามที่สำรวจในขั้นตอนเสริมด้านล่าง

จากนั้นเพียงเรียกใช้ฟังก์ชันที่ใช้ใน TravelService

async  createTravel(userId: String) {
	this.travelService.addEmptyTravel(userId);
}

deleteTravel(travelId: String) {
	this.travelService.deleteData(`travels/${travelId}`)
}

ตอนนี้หน้า My Travels ควรจะใช้งานได้แล้ว! ดูว่าเกิดอะไรขึ้นในโปรแกรมจำลอง Firestore เมื่อคุณสร้างโพสต์ท่องเที่ยวใหม่

จากนั้นทำซ้ำสำหรับฟังก์ชันการอัปเดตใน /src/app/pages/edit-travels/edit-travels.component.ts :

travelService: TravelService = inject(TravelService)
travelId = this.activatedRoute.snapshot.paramMap.get('travelId');
travelData$: Observable<Travel>;
stopsData$: Observable<Stop[]>;

constructor() {
	this.travelData$ = this.travelService.getDocData(`travels/${this.travelId}`) as  Observable<Travel>
	this.stopsData$ = this.travelService.getCollectionData(`travels/${this.travelId}/stops`) as  Observable<Stop[]>
}

updateCurrentTravel(travel: Partial<Travel>) {
	this.travelService.updateData(`travels${this.travelId}`, travel)
}

  

updateCurrentStop(stop: Partial<Stop>) {
	stop.type = stop.type?.toString();
	this.travelService.updateData(`travels${this.travelId}/stops/${stop.id}`, stop)
}

  

addStop() {
	if (!this.travelId) return;
	this.travelService.addStop(this.travelId);
}

deleteStop(stopId: string) {
	if (!this.travelId || !stopId) {
		return;
	}
	this.travelService.deleteData(`travels${this.travelId}/stops/${stopId}`)
	this.stopsData$ = this.travelService.getCollectionData(`travels${this.travelId}/stops`) as  Observable<Stop[]>

}

8. การกำหนดค่าที่เก็บข้อมูล

ตอนนี้คุณจะใช้พื้นที่เก็บข้อมูลเพื่อจัดเก็บรูปภาพและสื่อประเภทอื่นๆ

Cloud Firestore เหมาะที่สุดในการจัดเก็บข้อมูลที่มีโครงสร้าง เช่น ออบเจ็กต์ JSON Cloud Storage ได้รับการออกแบบมาเพื่อจัดเก็บไฟล์หรือ Blob ในแอปนี้ คุณจะใช้แอปนี้เพื่อให้ผู้ใช้สามารถแชร์รูปภาพการเดินทางของตนได้

ในทำนองเดียวกันกับ Firestore การจัดเก็บและอัปเดตไฟล์ด้วย Storage จำเป็นต้องมีตัวระบุที่ไม่ซ้ำกันสำหรับแต่ละไฟล์

ลองใช้ฟังก์ชั่นใน TraveService มาใช้:

กำลังอัพโหลดไฟล์

ไปที่ src/app/services/travel.service.ts และแทรก Storage จาก AngularFire:

export  class  TravelService {
firestore: Firestore = inject(Firestore);
auth: Auth = inject(Auth);
storage: Storage = inject(Storage);

และใช้ฟังก์ชันอัพโหลด:

async  uploadToStorage(path: string, input: HTMLInputElement, contentType: any) {
	if (!input.files) return  null
	const  files: FileList = input.files;
		for (let  i = 0; i  <  files.length; i++) {
			const  file = files.item(i);
			if (file) {
				const  imagePath = `${path}/${file.name}`
				const  storageRef = ref(this.storage, imagePath);
				await  uploadBytesResumable(storageRef, file, contentType);
				return  await  getDownloadURL(storageRef);
			}
		}
	return  null;
}

ข้อแตกต่างหลักระหว่างการเข้าถึงเอกสารจาก Firestore และไฟล์จาก Cloud Storage คือ แม้ว่าทั้งคู่จะติดตามเส้นทางที่มีโครงสร้างของโฟลเดอร์ แต่ URL ฐานและการรวมเส้นทางจะได้รับผ่าน getDownloadURL ซึ่งสามารถจัดเก็บและใช้ใน ไฟล์.

การใช้ฟังก์ชันในแอป

ไปที่ src/app/components/edit-stop/edit-stop.component.ts และเรียกใช้ฟังก์ชันอัปโหลดโดยใช้:

	async  uploadFile(file: HTMLInputElement, stop: Partial<Stop>) {
	const  path = `/travels/${this.travelId}/stops/${stop.id}`
	const  url = await  this.travelService.uploadToStorage(path, file, {contentType:  'image/png'});
	stop.image = url ? url : '';
	this.travelService.updateData(path, stop);
}

เมื่ออัปโหลดรูปภาพ ไฟล์สื่อจะถูกอัปโหลดไปยังที่เก็บข้อมูล และ URL จะถูกจัดเก็บไว้ในเอกสารใน Firestore

9. การปรับใช้แอปพลิเคชัน

ตอนนี้เราพร้อมที่จะปรับใช้แอปพลิเคชันแล้ว!

คัดลอกการกำหนดค่า firebase จาก src/environments/environment.ts ไปยัง src/environments/environment.prod.ts และรัน:

firebase deploy

คุณควรเห็นสิ่งนี้:

✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

=== Deploying to 'friendly-travels-b6a4b'...

i  deploying storage, firestore, hosting
i  firebase.storage: checking storage.rules for compilation errors...
✔  firebase.storage: rules file storage.rules compiled successfully
i  firestore: reading indexes from firestore.indexes.json...
i  cloud.firestore: checking firestore.rules for compilation errors...
✔  cloud.firestore: rules file firestore.rules compiled successfully
i  storage: latest version of storage.rules already up to date, skipping upload...
i  firestore: deploying indexes...
i  firestore: latest version of firestore.rules already up to date, skipping upload...
✔  firestore: deployed indexes in firestore.indexes.json successfully for (default) database
i  hosting[friendly-travels-b6a4b]: beginning deploy...
i  hosting[friendly-travels-b6a4b]: found 6 files in .firebase/friendly-travels-b6a4b/hosting
✔  hosting[friendly-travels-b6a4b]: file upload complete
✔  storage: released rules storage.rules to firebase.storage
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendly-travels-b6a4b]: finalizing version...
✔  hosting[friendly-travels-b6a4b]: version finalized
i  hosting[friendly-travels-b6a4b]: releasing new version...
✔  hosting[friendly-travels-b6a4b]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendly-travels-b6a4b/overview
Hosting URL: https://friendly-travels-b6a4b.web.app

10. ขอแสดงความยินดี!

ตอนนี้ใบสมัครของคุณควรเสร็จสมบูรณ์และปรับใช้กับ Firebase Hosting แล้ว ข้อมูลและการวิเคราะห์ทั้งหมดจะสามารถเข้าถึงได้ในคอนโซล Firebase ของคุณ

หากต้องการคุณสมบัติเพิ่มเติมเกี่ยวกับ AngularFire ฟังก์ชัน กฎความปลอดภัย อย่าลืมตรวจสอบขั้นตอนเสริมด้านล่าง รวมถึง Firebase Codelabs อื่นๆ

11. ตัวเลือกเสริม: เจ้าหน้าที่ตรวจสอบสิทธิ์ AngularFire

นอกเหนือจาก Firebase Authentication แล้ว AngularFire ยังเสนอการป้องกันตามการตรวจสอบสิทธิ์บนเส้นทาง เพื่อให้สามารถเปลี่ยนเส้นทางผู้ใช้ที่มีสิทธิ์เข้าถึงไม่เพียงพอได้ ซึ่งจะช่วยปกป้องแอปจากผู้ใช้ที่เข้าถึงข้อมูลที่ได้รับการคุ้มครอง

ใน src/app/app-routing.module.ts ให้นำเข้า

import {AuthGuard, redirectLoggedInTo, redirectUnauthorizedTo} from  '@angular/fire/auth-guard'

จากนั้นคุณสามารถกำหนดฟังก์ชันต่างๆ ได้ว่าเมื่อใด และที่ใดที่ผู้ใช้ควรถูกเปลี่ยนเส้นทางไปยังบางหน้า:

const  redirectUnauthorizedToLogin = () =>  redirectUnauthorizedTo(['signin']);
const  redirectLoggedInToTravels = () =>  redirectLoggedInTo(['my-travels']);

จากนั้นเพียงเพิ่มลงในเส้นทางของคุณ:

const  routes: Routes = [
	{path:  '', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'signin', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'my-travels', component:  MyTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
	{path:  'edit/:travelId', component:  EditTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
];

12. ทางเลือก: กฎความปลอดภัย

ทั้ง Firestore และ Cloud Storage ใช้กฎความปลอดภัย ( firestore.rules และ security.rules ตามลำดับ) เพื่อบังคับใช้ความปลอดภัยและตรวจสอบข้อมูล

ในขณะนี้ ข้อมูล Firestore และ Storage เปิดให้เข้าถึงเพื่ออ่านและเขียน แต่คุณคงไม่อยากให้คนอื่นเปลี่ยนโพสต์ของผู้อื่น! คุณสามารถใช้กฎความปลอดภัยเพื่อจำกัดการเข้าถึงคอลเลกชันและเอกสารของคุณได้

กฎของร้านดับเพลิง

หากต้องการอนุญาตให้เฉพาะผู้ใช้ที่ได้รับการรับรองความถูกต้องสามารถดูโพสต์การเดินทางได้ ให้ไปที่ไฟล์ firestore.rules และเพิ่ม:

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/travels  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
	}
}

กฎความปลอดภัยยังสามารถใช้เพื่อตรวจสอบข้อมูลได้:

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/posts  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
		&&  "author"  in  request.resource.data
		&&  "text"  in  request.resource.data
		&&  "timestamp"  in  request.resource.data;
	}
}

กฎการจัดเก็บ

ในทำนองเดียวกัน เราสามารถใช้กฎความปลอดภัยเพื่อบังคับใช้การเข้าถึงฐานข้อมูลหน่วยเก็บข้อมูลใน storage.rules โปรดทราบว่าเรายังสามารถใช้ฟังก์ชันสำหรับการตรวจสอบที่ซับซ้อนมากขึ้นได้:

rules_version  =  '2';

function  isImageBelowMaxSize(maxSizeMB)  {
	return  request.resource.size  <  maxSizeMB  *  1024  *  1024
		&&  request.resource.contentType.matches('image/.*');
}

 service  firebase.storage  {
	match  /b/{bucket}/o  {
		match  /{userId}/{postId}/{filename}  {
			allow  write:  if  request.auth  !=  null
			&&  request.auth.uid  ==  userId  &&  isImageBelowMaxSize(5);
			allow  read;
		}
	}
}