Lớp học lập trình về Khung web Angular của Firebase

1. Nội dung bạn sẽ tạo

Trong lớp học lập trình này, bạn sẽ xây dựng một blog du lịch có bản đồ cộng tác theo thời gian thực sử dụng tính năng mới nhất trong thư viện Angular của chúng tôi: AngularFire. Ứng dụng web hoàn thiện sẽ có một blog du lịch, nơi bạn có thể tải hình ảnh lên từng địa điểm mà bạn đã đến.

AngularFire sẽ được dùng để xây dựng ứng dụng web, Bộ mô phỏng để kiểm thử cục bộ, Xác thực để theo dõi dữ liệu người dùng, Firestore và Storage để lưu trữ dữ liệu và nội dung nghe nhìn, được hỗ trợ bởi Cloud Functions và cuối cùng là tính năng Lưu trữ Firebase để triển khai ứng dụng.

Kiến thức bạn sẽ học được

  • Cách phát triển cục bộ bằng các sản phẩm của Firebase bằng Bộ mô phỏng
  • Cách cải thiện ứng dụng web bằng AngularFire
  • Cách duy trì dữ liệu trong Firestore
  • Cách lưu trữ nội dung nghe nhìn trong Bộ nhớ
  • Cách triển khai ứng dụng của bạn cho tính năng Lưu trữ Firebase
  • Cách sử dụng Cloud Functions để tương tác với cơ sở dữ liệu và API

Bạn cần có

  • Node.js phiên bản 10 trở lên
  • Tài khoản Google để tạo và quản lý Dự án Firebase của bạn
  • Firebase CLI phiên bản 11.14.2 trở lên
  • Một trình duyệt mà bạn chọn, chẳng hạn như Chrome
  • Hiểu biết cơ bản về Angular và JavaScript

2. Nhận mã mẫu

Sao chép kho lưu trữ GitHub của lớp học lập trình từ dòng lệnh:

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

Ngoài ra, nếu chưa cài đặt git, bạn có thể tải kho lưu trữ xuống dưới dạng tệp ZIP.

Kho lưu trữ GitHub chứa các dự án mẫu dành cho nhiều nền tảng.

Lớp học lập trình này chỉ sử dụng kho lưu trữ webframework:

  • 📁 webframework: Mã khởi đầu mà bạn sẽ xây dựng trong lớp học lập trình này.

Cài đặt phần phụ thuộc

Sau khi sao chép, hãy cài đặt các phần phụ thuộc trong thư mục gốc và thư mục functions trước khi tạo ứng dụng web.

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

Cài đặt Firebase CLI

Cài đặt Firebase CLI bằng lệnh sau trong thiết bị đầu cuối:

npm install -g firebase-tools

Kiểm tra kỹ để đảm bảo phiên bản Firebase CLI của bạn lớn hơn 11.14.2 bằng cách sử dụng:

firebase  --version

Nếu phiên bản của bạn thấp hơn 11.14.2, vui lòng cập nhật bằng:

npm update firebase-tools

3. Tạo và thiết lập dự án Firebase

Tạo dự án Firebase

  1. Đăng nhập vào Firebase.
  2. Trong bảng điều khiển của Firebase, hãy nhấp vào Thêm dự án, sau đó đặt tên cho dự án Firebase của bạn là <your-project>. Ghi nhớ mã dự án cho dự án Firebase của bạn.
  3. Nhấp vào Create Project (Tạo dự án).

Lưu ý quan trọng: Dự án Firebase của bạn sẽ có tên là <your-project>, nhưng Firebase sẽ tự động chỉ định cho dự án đó một Mã dự án duy nhất ở dạng <your-project>-1234. Giá trị nhận dạng duy nhất này là cách dự án của bạn thực sự được xác định (kể cả trong CLI), trong khi <your-project> chỉ là tên hiển thị.

Ứng dụng mà chúng ta sắp xây dựng sử dụng các sản phẩm của Firebase có sẵn cho các ứng dụng web:

  • Xác thực Firebase để dễ dàng cho phép người dùng đăng nhập vào ứng dụng của bạn.
  • Cloud Firestore để lưu dữ liệu có cấu trúc lên đám mây và nhận thông báo tức thì khi dữ liệu thay đổi.
  • Cloud Storage cho Firebase để lưu tệp trên đám mây.
  • Lưu trữ Firebase để lưu trữ và phân phát tài sản của bạn.
  • Các hàm để tương tác với API nội bộ và API bên ngoài.

Một số sản phẩm trong số này cần cấu hình đặc biệt hoặc cần được bật bằng cách sử dụng bảng điều khiển của Firebase.

Thêm ứng dụng web Firebase vào dự án

  1. Nhấp vào biểu tượng web để tạo ứng dụng web Firebase mới.
  2. Ở bước tiếp theo, bạn sẽ thấy một đối tượng cấu hình. Sao chép nội dung của đối tượng này vào tệp environments/environment.ts.

Bật tính năng đăng nhập cho tính năng Xác thực Firebase của Google

Để cho phép người dùng đăng nhập vào ứng dụng web bằng Tài khoản Google của họ, chúng tôi sẽ sử dụng phương thức đăng nhập là Google.

Cách bật tính năng đăng nhập bằng Google:

  1. Trong bảng điều khiển của Firebase, hãy tìm mục Build (Tạo) trên bảng điều khiển bên trái.
  2. Nhấp vào Xác thực, sau đó nhấp vào thẻ Phương thức đăng nhập (hoặc nhấp vào đây để chuyển thẳng đến đó).
  3. Bật nhà cung cấp dịch vụ đăng nhập Google, sau đó nhấp vào Save (Lưu).
  4. Đặt tên công khai của ứng dụng là <your-project-name> rồi chọn Email hỗ trợ dự án từ trình đơn thả xuống.

Bật Cloud Firestore

  1. Trong phần Build (Tạo) trên bảng điều khiển của Firebase, hãy nhấp vào Firestore Database (Cơ sở dữ liệu khôi phục).
  2. Nhấp vào Create Database (Tạo cơ sở dữ liệu) trong ngăn Cloud Firestore.
  3. Thiết lập vị trí lưu trữ dữ liệu của bạn trên Cloud Firestore. Bạn có thể để cài đặt này là mặc định hoặc chọn một khu vực gần bạn.

Bật Cloud Storage

Ứng dụng web sử dụng Cloud Storage cho Firebase để lưu trữ, tải lên và chia sẻ hình ảnh.

  1. Trong phần Build (Tạo) của bảng điều khiển của Firebase, hãy nhấp vào Storage (Bộ nhớ).
  2. Nếu không có nút Bắt đầu, thì điều đó có nghĩa là Cloud Storage đã

bật lên, và bạn không cần làm theo các bước bên dưới.

  1. Nhấp vào Bắt đầu.
  2. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật cho dự án Firebase của bạn, rồi nhấp vào Tiếp theo.
  3. Vị trí trong Cloud Storage được chọn sẵn cùng với khu vực mà bạn đã chọn cho cơ sở dữ liệu Cloud Firestore của mình. Nhấp vào Xong để hoàn tất quá trình thiết lập.

Với quy tắc bảo mật mặc định, bất kỳ người dùng đã xác thực nào cũng có thể ghi mọi nội dung vào Cloud Storage. Chúng ta sẽ tăng cường bảo mật cho bộ nhớ sau trong lớp học lập trình này.

4. Kết nối với dự án Firebase của bạn

Giao diện dòng lệnh (CLI) Firebase cho phép bạn sử dụng tính năng Lưu trữ Firebase để phân phát ứng dụng web cục bộ, cũng như triển khai ứng dụng web cho dự án Firebase của bạn.

Đảm bảo dòng lệnh của bạn đang truy cập vào thư mục webframework cục bộ của ứng dụng.

Kết nối mã ứng dụng web với dự án Firebase của bạn. Trước tiên, hãy đăng nhập vào Firebase CLI trong dòng lệnh:

firebase login

Tiếp theo, hãy chạy lệnh sau để tạo một email đại diện cho dự án. Thay thế $YOUR_PROJECT_ID bằng mã dự án Firebase của bạn.

firebase  use  $YOUR_PROJECT_ID

Thêm AngularFire

Để thêm AngularFire vào ứng dụng, hãy chạy lệnh:

ng add @angular/fire

Sau đó, làm theo hướng dẫn dòng lệnh rồi chọn các tính năng có trong dự án Firebase của bạn.

Chạy Firebase

Để khởi chạy dự án Firebase, hãy chạy:

firebase init

Sau đó, làm theo lời nhắc dòng lệnh, hãy chọn các tính năng và trình mô phỏng được dùng trong dự án Firebase của bạn.

Khởi động trình mô phỏng

Từ thư mục webframework, hãy chạy lệnh sau để khởi động trình mô phỏng:

firebase  emulators:start

Cuối cùng, bạn sẽ thấy như sau:

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

Sau khi bạn thấy thông báo ✔All emulators ready!, tức là trình mô phỏng đã sẵn sàng để sử dụng.

Bạn sẽ thấy giao diện người dùng của ứng dụng du lịch. Giao diện người dùng này (chưa) hoạt động:

Giờ hãy bắt đầu xây dựng!

5. Kết nối ứng dụng web với trình mô phỏng

Dựa trên bảng trong nhật ký của trình mô phỏng, trình mô phỏng Cloud Firestore đang nghe trên cổng 8080 và trình mô phỏng xác thực đang nghe trên cổng 9099.

Mở EmulatorUI

Trong trình duyệt web, hãy chuyển đến http://127.0.0.1:4000/. Bạn sẽ thấy giao diện người dùng của Bộ mô phỏng.

Định tuyến ứng dụng để dùng trình mô phỏng

Trong src/app/app.module.ts, hãy thêm mã sau vào danh sách nhập của 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;
		}),
		...
	]

Ứng dụng hiện được định cấu hình để sử dụng trình mô phỏng cục bộ, cho phép thực hiện kiểm thử và phát triển trên máy.

6. Đang thêm phương thức xác thực

Giờ đây, trình mô phỏng đã được thiết lập cho ứng dụng, chúng ta có thể thêm các tính năng Xác thực để đảm bảo từng người dùng đều đăng nhập trước khi đăng thông báo.

Để thực hiện việc này, chúng ta có thể nhập các hàm signin trực tiếp từ AngularFire và theo dõi trạng thái xác thực của người dùng bằng hàm authState. Sửa đổi các hàm của trang đăng nhập để trang kiểm tra trạng thái xác thực của người dùng khi tải.

Chèn tính năng xác thực AngularFire

Trong src/app/pages/login-page/login-page.component.ts, hãy nhập Auth từ @angular/fire/auth rồi chèn vào LoginPageComponent. Các nhà cung cấp dịch vụ xác thực (như Google) và các hàm như signin, signout cũng có thể được nhập trực tiếp từ cùng một gói và được sử dụng trong ứng dụng.

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);
		})
	}
}

Bây giờ, trang đăng nhập đã hoạt động! Hãy thử đăng nhập và xem kết quả trong Trình mô phỏng xác thực.

7. Định cấu hình Firestore

Ở bước này, bạn sẽ thêm chức năng đăng và cập nhật các bài đăng trên blog du lịch được lưu trữ trong Firestore.

Tương tự như Xác thực, các hàm Firestore được đóng gói sẵn từ AngularFire. Mỗi tài liệu thuộc một tập hợp và mỗi tài liệu cũng có thể chứa các tập hợp lồng nhau. Bạn phải biết path của tài liệu trong Firestore để tạo và cập nhật một bài đăng trên blog du lịch.

Triển khai TravelService

Vì nhiều trang khác nhau sẽ cần đọc và cập nhật tài liệu trên Firestore trong ứng dụng web, nên chúng ta có thể triển khai các hàm này trong src/app/services/travel.service.ts để tránh việc chèn cùng một hàm AngularFire nhiều lần vào mỗi trang.

Bắt đầu bằng việc chèn Auth, tương tự như bước trước, cũng như Firestore vào dịch vụ của chúng ta. Việc xác định một đối tượng user$ có thể quan sát để theo dõi trạng thái xác thực hiện tại cũng rất hữu ích.

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);

Thêm một bài đăng du lịch

Các bài đăng du lịch sẽ tồn tại dưới dạng tài liệu được lưu trữ trong Firestore và vì tài liệu phải tồn tại trong các bộ sưu tập, nên bộ sưu tập chứa tất cả các bài đăng du lịch sẽ được đặt tên là travels. Do đó, đường dẫn của mọi bài đăng du lịch sẽ là travels/

Bằng cách sử dụng hàm addDoc từ AngularFire, bạn có thể chèn một đối tượng vào một tập hợp:

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;

	})
}

Cập nhật và xoá dữ liệu

Dựa trên uid của bất kỳ bài đăng du lịch nào, bạn có thể suy ra đường dẫn của tài liệu được lưu trữ trong Firestore. Sau đó, bạn có thể đọc, cập nhật hoặc xoá đường dẫn này bằng các hàm updateFocdeleteDoc của 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)
}

Đọc dữ liệu dưới dạng đối tượng có thể quan sát

Vì các bài đăng du lịch và điểm dừng trên đường đi có thể được sửa đổi sau khi tạo, nên sẽ hữu ích hơn nếu nhận các đối tượng tài liệu dưới dạng đối tượng có thể quan sát, để đăng ký mọi thay đổi được thực hiện. Chức năng này do các hàm docDatacollectionData của @angular/fire/firestore cung cấp.

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[]
}

Thêm trạm dừng vào bài đăng du lịch

Bây giờ, các hoạt động của bưu điện du lịch đã được thiết lập, đã đến lúc xem xét các điểm dừng. Các điểm dừng này sẽ nằm trong một tập hợp con của một bưu điện du lịch như vậy: travels//stops/

Việc này gần giống với việc tạo một bài đăng về du lịch. Vì vậy, hãy thử thách bản thân tự triển khai bài đăng này hoặc xem cách triển khai dưới đây:

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

Hay quá! Các hàm Firestore đã được triển khai trong dịch vụ Du lịch, nên giờ đây bạn có thể thấy các hàm này trong thực tế.

Sử dụng các hàm Firestore trong ứng dụng

Chuyển đến src/app/pages/my-travels/my-travels.component.ts rồi chèn TravelService để sử dụng các hàm.

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

TravelService được gọi trong hàm khởi tạo để lấy một mảng Có thể quan sát cho tất cả các hành trình.

Trong trường hợp chỉ cần di chuyển của người dùng hiện tại, hãy sử dụng hàm query.

Các phương pháp khác để đảm bảo bảo mật bao gồm triển khai các quy tắc bảo mật hoặc sử dụng Cloud Functions với Firestore như được trình bày trong các bước không bắt buộc dưới đây

Sau đó, bạn chỉ cần gọi các hàm được triển khai trong TravelService.

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

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

Bây giờ, trang Du lịch của tôi đã hoạt động bình thường! Hãy xem điều gì xảy ra trong trình mô phỏng Firestore khi bạn tạo một bài đăng mới về du lịch.

Sau đó, lặp lại các hàm cập nhật trong /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. Định cấu hình bộ nhớ

Bây giờ, bạn sẽ triển khai Bộ nhớ để lưu trữ hình ảnh và các loại nội dung nghe nhìn khác.

Cloud Firestore là giải pháp phù hợp nhất để lưu trữ dữ liệu có cấu trúc, chẳng hạn như đối tượng JSON. Cloud Storage được thiết kế để lưu trữ các tệp hoặc blob. Trong ứng dụng này, bạn sẽ dùng thông tin đó để cho phép người dùng chia sẻ ảnh du lịch của họ.

Tương tự như vậy với Firestore, việc lưu trữ và cập nhật tệp bằng Bộ nhớ yêu cầu một giá trị nhận dạng duy nhất cho mỗi tệp.

Hãy triển khai các hàm trong TraveService:

Tải tệp lên

Chuyển đến src/app/services/travel.service.ts rồi chèn Bộ nhớ từ AngularFire:

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

Và triển khai chức năng tải lên:

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;
}

Điểm khác biệt chính giữa việc truy cập vào tài liệu qua Firestore và các tệp trên Cloud Storage là mặc dù cả hai đều đi theo đường dẫn có cấu trúc thư mục, nhưng tổ hợp URL cơ sở và đường dẫn sẽ được lấy thông qua getDownloadURL, sau đó có thể được lưu trữ và sử dụng trong tệp .

Dùng hàm này trong ứng dụng

Chuyển đến src/app/components/edit-stop/edit-stop.component.ts rồi gọi hàm tải lên bằng:

	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);
}

Khi hình ảnh được tải lên, tệp đa phương tiện sẽ được tải lên bộ nhớ và URL được lưu trữ tương ứng trong tài liệu trên Firestore.

9. Triển khai ứng dụng

Bây giờ, chúng ta đã sẵn sàng triển khai ứng dụng!

Sao chép các cấu hình firebase từ src/environments/environment.ts sang src/environments/environment.prod.ts rồi chạy:

firebase deploy

Bạn sẽ thấy như sau:

 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. Xin chúc mừng!

Bây giờ, ứng dụng của bạn đã được hoàn tất và được triển khai trong Lưu trữ Firebase! Giờ đây, bạn có thể truy cập vào tất cả dữ liệu và số liệu phân tích trong Bảng điều khiển của Firebase.

Để biết thêm các tính năng liên quan đến AngularFire, Hàm, quy tắc bảo mật, đừng quên xem các bước không bắt buộc bên dưới, cũng như các Lớp học lập trình về Firebase khác!

11. Không bắt buộc: Trình bảo vệ xác thực AngularFire

Cùng với tính năng Xác thực Firebase, AngularFire cũng cung cấp các biện pháp bảo vệ dựa trên xác thực trên các tuyến đường để những người dùng không có đủ quyền truy cập có thể được chuyển hướng. Điều này giúp bảo vệ ứng dụng khỏi việc người dùng truy cập vào dữ liệu được bảo vệ.

Trong src/app/app-routing.module.ts, hãy nhập

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

Sau đó, bạn có thể xác định các hàm như thời điểm và nơi người dùng nên được chuyển hướng đến trên một số trang nhất định:

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

Sau đó, chỉ cần thêm chúng vào tuyến đường của bạn:

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. Không bắt buộc: quy tắc bảo mật

Cả Firestore và Cloud Storage đều sử dụng các quy tắc bảo mật (tương ứng là firestore.rulessecurity.rules) để thực thi khả năng bảo mật và xác thực dữ liệu.

Hiện tại, dữ liệu Firestore và Storage có quyền truy cập mở để đọc và ghi, nhưng bạn không muốn mọi người tự mình thay đổi bài đăng! Bạn có thể sử dụng các quy tắc bảo mật để hạn chế quyền truy cập vào bộ sưu tập và tài liệu của mình.

Quy tắc của Firestore

Để chỉ cho phép người dùng đã xác thực xem các bài đăng về du lịch, hãy chuyển đến tệp firestore.rules rồi thêm:

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;
	}
}

Quy tắc bảo mật cũng có thể dùng để xác thực dữ liệu:

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;
	}
}

Quy tắc lưu trữ

Tương tự, chúng ta có thể sử dụng các quy tắc bảo mật để thực thi quyền truy cập vào cơ sở dữ liệu lưu trữ trong storage.rules. Xin lưu ý rằng chúng ta cũng có thể dùng các hàm cho những quy trình kiểm tra phức tạp hơn:

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;
		}
	}
}