1. สิ่งที่คุณจะสร้าง
ในโค้ดแล็บนี้ คุณจะได้สร้างบล็อกท่องเที่ยวพร้อมแผนที่ที่ทำงานร่วมกันแบบเรียลไทม์ด้วยข้อมูลล่าสุดจากไลบรารี Angular ของเรา นั่นคือ AngularFire เว็บแอปสุดท้ายจะประกอบด้วยบล็อกท่องเที่ยวที่คุณสามารถอัปโหลดรูปภาพไปยังแต่ละสถานที่ที่คุณเคยไป
เราจะใช้ AngularFire เพื่อสร้างเว็บแอป, ชุดโปรแกรมจำลองสำหรับการทดสอบในเครื่อง, การตรวจสอบสิทธิ์เพื่อติดตามข้อมูลผู้ใช้, Firestore และพื้นที่เก็บข้อมูลเพื่อจัดเก็บข้อมูลและสื่อ โดยใช้ Cloud Functions และสุดท้ายคือ Firebase Hosting เพื่อติดตั้งใช้งานแอป
สิ่งที่คุณจะได้เรียนรู้
- วิธีพัฒนาด้วยผลิตภัณฑ์ Firebase ในเครื่องด้วย Emulator Suite
- วิธีเพิ่มประสิทธิภาพเว็บแอปด้วย AngularFire
- วิธีจัดเก็บข้อมูลใน Firestore อย่างถาวร
- วิธีจัดเก็บสื่ออย่างถาวรในพื้นที่เก็บข้อมูล
- วิธีทําให้แอปใช้งานได้กับโฮสติ้งของ Firebase
- วิธีใช้ 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: โค้ดเริ่มต้นที่คุณจะใช้ต่อยอดใน 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
- ลงชื่อเข้าใช้คอนโซล Firebase โดยใช้บัญชี Google
- คลิกปุ่มเพื่อสร้างโปรเจ็กต์ใหม่ แล้วป้อนชื่อโปรเจ็กต์ (เช่น
FriendlyChat
)
- คลิกต่อไป
- หากได้รับแจ้ง ให้อ่านและยอมรับข้อกำหนดของ Firebase แล้วคลิกต่อไป
- (ไม่บังคับ) เปิดใช้ความช่วยเหลือจาก AI ในคอนโซล Firebase (เรียกว่า "Gemini ใน Firebase")
- สำหรับ Codelab นี้ คุณไม่จำเป็นต้องใช้ Google Analytics ดังนั้นให้ปิดตัวเลือก Google Analytics
- คลิกสร้างโปรเจ็กต์ รอให้ระบบจัดสรรโปรเจ็กต์ แล้วคลิกดำเนินการต่อ
เพิ่มเว็บแอป Firebase ลงในโปรเจ็กต์
- คลิกไอคอนเว็บเพื่อสร้างเว็บแอป Firebase ใหม่
- ในขั้นตอนถัดไป คุณจะเห็นออบเจ็กต์การกำหนดค่า คัดลอกเนื้อหาของออบเจ็กต์นี้ลงในไฟล์
environments/environment.ts
ตั้งค่าผลิตภัณฑ์ Firebase
แอปพลิเคชันที่เราจะสร้างใช้ผลิตภัณฑ์ Firebase ที่พร้อมใช้งานสำหรับเว็บแอป ดังนี้
- การตรวจสอบสิทธิ์ Firebase เพื่อให้ผู้ใช้ลงชื่อเข้าใช้แอปของคุณได้อย่างง่ายดาย
- Cloud Firestore เพื่อบันทึกข้อมูลที่มีโครงสร้างไว้ในระบบคลาวด์และรับการแจ้งเตือนทันทีเมื่อข้อมูลมีการเปลี่ยนแปลง
- Cloud Storage for Firebase เพื่อบันทึกไฟล์ในระบบคลาวด์
- Firebase Hosting เพื่อโฮสต์และแสดงเนื้อหา
- ฟังก์ชันสำหรับโต้ตอบกับ API ภายในและภายนอก
ผลิตภัณฑ์บางอย่างเหล่านี้ต้องมีการกำหนดค่าพิเศษหรือต้องเปิดใช้โดยใช้คอนโซล Firebase
เปิดใช้การลงชื่อเข้าใช้ด้วย Google สำหรับการตรวจสอบสิทธิ์ Firebase
หากต้องการอนุญาตให้ผู้ใช้ลงชื่อเข้าใช้เว็บแอปด้วยบัญชี Google เราจะใช้วิธีการลงชื่อเข้าใช้ของ Google
วิธีเปิดใช้การลงชื่อเข้าใช้ Google
- ในคอนโซล Firebase ให้หาส่วนสร้างในแผงด้านซ้าย
- คลิก Authentication แล้วคลิกแท็บวิธีการลงชื่อเข้าใช้ (หรือคลิกที่นี่เพื่อไปที่แท็บดังกล่าวโดยตรง)
- เปิดใช้ผู้ให้บริการลงชื่อเข้าใช้ Google แล้วคลิกบันทึก
- ตั้งชื่อที่เปิดเผยต่อสาธารณะของแอปเป็น <your-project-name> แล้วเลือกอีเมลสนับสนุนโปรเจ็กต์จากเมนูแบบเลื่อนลง
เปิดใช้ Cloud Firestore
- ในส่วนสร้างของคอนโซล Firebase ให้คลิกฐานข้อมูล Firestore
- คลิกสร้างฐานข้อมูลในแผง Cloud Firestore
- ตั้งค่าตำแหน่งที่จัดเก็บข้อมูล Cloud Firestore คุณจะปล่อยให้เป็นค่าเริ่มต้นหรือเลือกภูมิภาคที่อยู่ใกล้คุณก็ได้
เปิดใช้ Cloud Storage
เว็บแอปใช้ Cloud Storage for Firebase เพื่อจัดเก็บ อัปโหลด และแชร์รูปภาพ
- ในส่วนสร้างของคอนโซล Firebase ให้คลิก Storage
- หากไม่มีปุ่มเริ่มต้นใช้งาน แสดงว่า Cloud Storage
เปิดใช้ไว้แล้ว และคุณไม่จำเป็นต้องทำตามขั้นตอนด้านล่าง
- คลิกเริ่มต้นใช้งาน
- อ่านข้อจำกัดความรับผิดเกี่ยวกับกฎการรักษาความปลอดภัยสำหรับโปรเจ็กต์ Firebase แล้วคลิกถัดไป
- ระบบจะเลือกตำแหน่ง Cloud Storage ไว้ล่วงหน้าโดยใช้ภูมิภาคเดียวกันกับที่คุณเลือกสำหรับฐานข้อมูล Cloud Firestore คลิกเสร็จสิ้นเพื่อตั้งค่าให้เสร็จสมบูรณ์
กฎความปลอดภัยเริ่มต้นช่วยให้ผู้ใช้ที่ตรวจสอบสิทธิ์แล้วทุกคนเขียนข้อมูลใดก็ได้ไปยัง Cloud Storage เราจะทำให้พื้นที่เก็บข้อมูลปลอดภัยยิ่งขึ้นในโค้ดแล็บนี้ในภายหลัง
4. เชื่อมต่อกับโปรเจ็กต์ Firebase
อินเทอร์เฟซบรรทัดคำสั่ง (CLI) ของ Firebase ช่วยให้คุณใช้ Firebase Hosting เพื่อแสดงเว็บแอปในเครื่อง รวมถึงทำให้เว็บแอปใช้งานได้ในโปรเจ็กต์ Firebase
ตรวจสอบว่าบรรทัดคำสั่งเข้าถึงไดเรกทอรี webframework
ในเครื่องของแอป
เชื่อมต่อโค้ดเว็บแอปกับโปรเจ็กต์ Firebase ก่อนอื่น ให้เข้าสู่ระบบ Firebase CLI ในบรรทัดคำสั่งโดยทำดังนี้
firebase login
จากนั้นเรียกใช้คำสั่งต่อไปนี้เพื่อสร้างชื่อแทนของโปรเจ็กต์ แทนที่ $YOUR_PROJECT_ID
ด้วยรหัสของโปรเจ็กต์ Firebase
firebase use $YOUR_PROJECT_ID
เพิ่ม AngularFire
หากต้องการเพิ่ม 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/ คุณควรเห็น 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
แก้ไขฟังก์ชันหน้าเข้าสู่ระบบเพื่อให้หน้าเว็บตรวจสอบสถานะการให้สิทธิ์ของผู้ใช้เมื่อโหลด
การแทรก Auth ของ AngularFire
ใน 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);
})
}
}
ตอนนี้หน้าเข้าสู่ระบบใช้งานได้แล้ว ลองเข้าสู่ระบบ แล้วดูผลลัพธ์ในโปรแกรมจำลองการตรวจสอบสิทธิ์
7. การกำหนดค่า Firestore
ในขั้นตอนนี้ คุณจะต้องเพิ่มฟังก์ชันการทำงานเพื่อโพสต์และอัปเดตบล็อกโพสต์ท่องเที่ยวที่จัดเก็บไว้ใน Firestore
ฟังก์ชัน Firestore จะมาพร้อมกับ AngularFire เช่นเดียวกับการตรวจสอบสิทธิ์ เอกสารแต่ละรายการจะอยู่ในคอลเล็กชัน และเอกสารแต่ละรายการจะมีคอลเล็กชันที่ซ้อนกันได้ด้วย คุณต้องทราบ path
ของเอกสารใน Firestore เพื่อสร้างและอัปเดตบล็อกโพสต์เกี่ยวกับการท่องเที่ยว
การใช้ TravelService
เนื่องจากหน้าเว็บหลายหน้าจะต้องอ่านและอัปเดตเอกสาร 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)
}
อ่านข้อมูลเป็น Observable
เนื่องจากโพสต์การเดินทางและจุดแวะพักระหว่างทางสามารถแก้ไขได้หลังจากสร้างแล้ว การรับออบเจ็กต์เอกสารเป็นออบเซอร์เวเบิลเพื่อติดตามการเปลี่ยนแปลงที่เกิดขึ้นจึงมีประโยชน์มากกว่า ฟังก์ชันนี้ให้บริการโดยฟังก์ชัน 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/
ซึ่งแทบจะเหมือนกับการสร้างโพสต์การเดินทางเลย ดังนั้นลองนำไปใช้ด้วยตนเอง หรือดูการติดตั้งใช้งานด้านล่าง
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
เพื่อใช้ฟังก์ชันของ src/app/pages/my-travels/my-travels.component.ts
travelService = inject(TravelService);
travelsData$: Observable<Travel[]>;
stopsList$!: Observable<Stop[]>;
constructor() {
this.travelsData$ = this.travelService.getCollectionData(`travels`) as Observable<Travel[]>
}
TravelService
จะเรียกใช้ในเครื่องมือสร้างเพื่อรับอาร์เรย์ที่สังเกตได้ของการเดินทางทั้งหมด
ในกรณีที่ต้องการเฉพาะการเดินทางของผู้ใช้ปัจจุบัน ให้ใช้query
ฟังก์ชัน
วิธีอื่นๆ ในการรับประกันความปลอดภัย ได้แก่ การใช้กฎความปลอดภัย หรือการใช้ Cloud Functions กับ Firestore ตามที่อธิบายไว้ในขั้นตอนที่ไม่บังคับด้านล่าง
จากนั้นเพียงเรียกใช้ฟังก์ชันที่ใช้งานใน TravelService
async createTravel(userId: String) {
this.travelService.addEmptyTravel(userId);
}
deleteTravel(travelId: String) {
this.travelService.deleteData(`travels/${travelId}`)
}
ตอนนี้หน้าการเดินทางของฉันควรใช้งานได้แล้ว ดูสิ่งที่เกิดขึ้นในโปรแกรมจำลอง 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. การกำหนดค่าพื้นที่เก็บข้อมูล
ตอนนี้คุณจะใช้ Storage เพื่อจัดเก็บรูปภาพและสื่อประเภทอื่นๆ
Cloud Firestore เหมาะที่สุดสำหรับการจัดเก็บ Structured Data เช่น ออบเจ็กต์ 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 คือแม้ว่าทั้ง 2 อย่างจะใช้เส้นทางที่มีโครงสร้างโฟลเดอร์เหมือนกัน แต่ระบบจะรับ 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, ฟังก์ชัน และกฎความปลอดภัย อย่าลืมดูขั้นตอนที่ไม่บังคับด้านล่าง รวมถึง Codelab ของ Firebase อื่นๆ ด้วย
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
หากต้องการอนุญาตให้เฉพาะผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ดูโพสต์การเดินทาง ให้ไปที่ไฟล์ 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;
}
}
}