1. สิ่งที่คุณจะสร้าง
ใน Codelab นี้ คุณจะได้สร้างบล็อกท่องเที่ยวพร้อมแผนที่การทำงานร่วมกันแบบเรียลไทม์ โดยมี AngularFire เป็นเวอร์ชันล่าสุดจากไลบรารี Angular ของเรา เว็บแอปสุดท้ายจะประกอบด้วยบล็อกท่องเที่ยวซึ่งคุณสามารถอัปโหลดรูปภาพไปยังสถานที่แต่ละแห่งที่คุณเดินทางไปได้
ระบบจะใช้ AngularFire เพื่อสร้างเว็บแอป, ชุดโปรแกรมจำลองสำหรับการทดสอบในเครื่อง, การตรวจสอบสิทธิ์เพื่อติดตามข้อมูลผู้ใช้, Firestore และพื้นที่เก็บข้อมูลเพื่อคงข้อมูลและสื่อซึ่งขับเคลื่อนโดย Cloud Functions และสุดท้ายคือโฮสติ้งของ Firebase เพื่อทำให้แอปใช้งานได้
สิ่งที่คุณจะได้เรียนรู้
- วิธีพัฒนาด้วยผลิตภัณฑ์ Firebase ในเครื่องด้วยชุดโปรแกรมจำลอง
- วิธีเพิ่มประสิทธิภาพเว็บแอปด้วย 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 ต่อไปนี้
- 📁 webframework: โค้ดเริ่มต้นที่คุณจะสร้างขึ้นในระหว่าง Codelab นี้
ติดตั้งการอ้างอิง
หลังจากโคลนแล้ว ให้ติดตั้งทรัพยากร Dependency ในโฟลเดอร์รูทและ 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
- ในคอนโซล Firebase ให้คลิกเพิ่มโปรเจ็กต์ แล้วตั้งชื่อโปรเจ็กต์ Firebase ของคุณว่า <your-project> จำรหัสโปรเจ็กต์ของโปรเจ็กต์ Firebase
- คลิกสร้างโปรเจ็กต์
สำคัญ: ระบบจะตั้งชื่อโปรเจ็กต์ Firebase ว่า <your-project> แต่ Firebase จะกำหนดรหัสโปรเจ็กต์ที่ไม่ซ้ำให้โดยอัตโนมัติในรูปแบบ <your-project>-1234 ตัวระบุที่ไม่ซ้ำกันนี้คือวิธีระบุโปรเจ็กต์จริง (รวมถึงใน CLI) ขณะที่ <your-project> เป็นเพียงชื่อที่แสดง
แอปพลิเคชันที่เราจะสร้างใช้ผลิตภัณฑ์ Firebase ที่พร้อมใช้งานสำหรับเว็บแอป
- การตรวจสอบสิทธิ์ของ Firebase เพื่อให้ผู้ใช้ลงชื่อเข้าใช้แอปได้ง่ายๆ
- Cloud Firestore เพื่อบันทึก Structured Data ในระบบคลาวด์และรับการแจ้งเตือนทันทีเมื่อข้อมูลมีการเปลี่ยนแปลง
- Cloud Storage for Firebase เพื่อบันทึกไฟล์ในระบบคลาวด์
- โฮสติ้งของ Firebase เพื่อโฮสต์และแสดงเนื้อหาของคุณ
- ฟังก์ชันเพื่อโต้ตอบกับ API ภายในและภายนอก
ผลิตภัณฑ์เหล่านี้บางรายการต้องมีการกำหนดค่าพิเศษหรือต้องเปิดใช้โดยใช้คอนโซล Firebase
เพิ่มเว็บแอป Firebase ลงในโปรเจ็กต์
- คลิกไอคอนเว็บเพื่อสร้างเว็บแอป Firebase ใหม่
- คุณจะเห็นออบเจ็กต์การกำหนดค่าในขั้นตอนถัดไป คัดลอกเนื้อหาของออบเจ็กต์นี้ลงในไฟล์
environments/environment.ts
เปิดใช้การตรวจสอบสิทธิ์การลงชื่อเข้าใช้สำหรับ Firebase ของ Google
หากต้องการอนุญาตให้ผู้ใช้ลงชื่อเข้าใช้เว็บแอปด้วยบัญชี Google เราจะใช้วิธีการลงชื่อเข้าใช้ Google
หากต้องการเปิดใช้การลงชื่อเข้าใช้ Google ให้ทำดังนี้
- ในคอนโซล Firebase ให้ค้นหาส่วนสร้างในแผงด้านซ้าย
- คลิกการตรวจสอบสิทธิ์ แล้วคลิกแท็บวิธีการลงชื่อเข้าใช้ (หรือคลิกที่นี่เพื่อไปยังแท็บนั้นโดยตรง)
- เปิดใช้ผู้ให้บริการการลงชื่อเข้าใช้ Google แล้วคลิกบันทึก
- ตั้งชื่อแอปที่เปิดเผยต่อสาธารณะเป็น <your-project-name> แล้วเลือกอีเมลสนับสนุนของโปรเจ็กต์จากเมนูแบบเลื่อนลง
เปิดใช้ Cloud Firestore
- ในส่วน Build ของคอนโซล Firebase ให้คลิก Firestore Database
- คลิกสร้างฐานข้อมูลในแผง Cloud Firestore
- ตั้งค่าตำแหน่งที่ใช้เก็บข้อมูล Cloud Firestore คุณจะปล่อยค่านี้เป็นค่าเริ่มต้นหรือเลือกภูมิภาคที่อยู่ใกล้ๆ ก็ได้
เปิดใช้ Cloud Storage
โดยเว็บแอปนี้ใช้ Cloud Storage for Firebase เพื่อจัดเก็บ อัปโหลด และแชร์รูปภาพ
- ในส่วนสร้างของคอนโซล Firebase ให้คลิกพื้นที่เก็บข้อมูล
- หากไม่มีปุ่มเริ่มต้นใช้งาน แสดงว่ามีพื้นที่เก็บข้อมูลระบบคลาวด์อยู่แล้ว
เปิดใช้อยู่แล้ว และคุณไม่จำเป็นต้องทำตามขั้นตอนด้านล่าง
- คลิกเริ่มต้นใช้งาน
- อ่านข้อจำกัดความรับผิดเกี่ยวกับกฎความปลอดภัยสำหรับโปรเจ็กต์ Firebase แล้วคลิกถัดไป
- ระบบจะเลือกตำแหน่ง Cloud Storage ล่วงหน้าด้วยภูมิภาคเดียวกับที่คุณเลือกสำหรับฐานข้อมูล Cloud Firestore คลิกเสร็จสิ้นเพื่อตั้งค่าให้เสร็จสมบูรณ์
เมื่อใช้กฎความปลอดภัยเริ่มต้น ผู้ใช้ที่ตรวจสอบสิทธิ์แล้วจะเขียนอะไรก็ได้ลงใน Cloud Storage เราจะทำให้พื้นที่เก็บข้อมูลปลอดภัยยิ่งขึ้นใน Codelab นี้
4. เชื่อมต่อกับโปรเจ็กต์ Firebase
อินเทอร์เฟซบรรทัดคำสั่ง (CLI) ของ Firebase ช่วยให้คุณใช้โฮสติ้งของ Firebase เพื่อแสดงเว็บแอปภายในเครื่อง รวมถึงทำให้เว็บแอปใช้งานได้ในโปรเจ็กต์ 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
แก้ไขฟังก์ชันหน้าการเข้าสู่ระบบเพื่อให้หน้าตรวจสอบสถานะการตรวจสอบสิทธิ์ของผู้ใช้ขณะโหลด
กำลังแทรกการตรวจสอบสิทธิ์ 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)
}
การอ่านข้อมูลเป็นแบบที่สังเกตได้
เนื่องจากโพสต์และป้ายจอดรถระหว่างทางสามารถแก้ไขได้หลังจากสร้าง การใช้ออบเจ็กต์เอกสารเป็นข้อมูลที่สังเกตได้จึงจะมีประโยชน์มากกว่าสำหรับการสมัครรับข้อมูลการเปลี่ยนแปลงที่เกิดขึ้น ฟังก์ชันนี้ให้บริการโดยฟังก์ชัน 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 ในบริการ Travel แล้ว คุณจึงดูการทํางานจริงได้ในตอนนี้
การใช้ฟังก์ชัน 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
วิธีอื่นๆ ในการดูแลความปลอดภัย ได้แก่ การใช้กฎความปลอดภัย หรือการใช้ Cloud Functions กับ 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 การจัดเก็บและการอัปเดตไฟล์ด้วยพื้นที่เก็บข้อมูลจะต้องมีตัวระบุที่ไม่ซ้ำกันสำหรับแต่ละไฟล์
มาใช้ฟังก์ชันใน 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 แล้ว ตอนนี้คุณจะเข้าถึงข้อมูลและการวิเคราะห์ทั้งหมดได้ในคอนโซล Firebase
สำหรับฟีเจอร์เพิ่มเติมเกี่ยวกับ AngularFire, ฟังก์ชัน, กฎความปลอดภัย โปรดอย่าลืมดูขั้นตอนทางเลือกด้านล่างนี้ รวมถึง Firebase Codelab อื่นๆ
11. ไม่บังคับ: ตัวป้องกันการตรวจสอบสิทธิ์ AngularFire
AngularFire ควบคู่กับการตรวจสอบสิทธิ์ Firebase แล้วยังมีการป้องกันที่อิงตามการตรวจสอบสิทธิ์บนเส้นทาง เพื่อให้ระบบเปลี่ยนเส้นทางผู้ใช้ที่มีสิทธิ์เข้าถึงไม่เพียงพอได้ วิธีนี้จะช่วยปกป้องแอปจากผู้ใช้ที่เข้าถึงข้อมูลที่มีการป้องกัน
ใน 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 และพื้นที่เก็บข้อมูลมีสิทธิ์แบบเปิดสำหรับอ่านและเขียน แต่คุณไม่ต้องการให้ผู้อื่นเปลี่ยนแปลง โพสต์ คุณจะใช้กฎความปลอดภัยเพื่อจำกัดการเข้าถึงคอลเล็กชันและเอกสารได้
กฎ 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;
}
}
}