Angular+FireStore生成计算字段的最佳实践:能否用RxJS实现?
completeName in Angular with RxJS + Firebase Firestore Hey there! Let's tackle your question about handling the completeName field—both your proposed options have their merits, but let's focus on implementing the RxJS client-side calculation approach you're curious about, plus weigh in on when each option makes sense.
First: Quick Pros & Cons of Your Two Options
1. Storing completeName in Firestore
- Pros: Super straightforward for server-side sorting (since you can order directly in your Firestore query) and no client-side computation needed.
- Cons: Creates redundant data, and you’ll have to ensure
completeNamestays in sync iffirstNameorsurnameever gets updated (either via client-side logic when editing, or Firestore Cloud Functions). For small datasets this is manageable, but it adds maintenance overhead.
2. Calculating completeName via RxJS (Client-Side)
- Pros: No data redundancy, single source of truth (only
firstNameandsurnameare stored), and no sync issues to worry about. Perfect for small datasets like you mentioned. - Cons: Sorting has to happen client-side (since Firestore can’t sort on a field that doesn’t exist in the database). But again, with small data volumes, this is totally performant.
Implementing the RxJS Approach
First, let’s fix your getAllEmployees method—you don’t need the async keyword here because valueChanges() returns an Observable, not a Promise. We’ll use RxJS’s map operator to transform the array of employees and inject the completeName field for each entry.
Step 1: Import the Required RxJS Operator
Make sure you have the map operator imported in your service file:
import { map } from 'rxjs/operators';
Step 2: Modify the Service Method
Update getAllEmployees to pipe the observable and compute completeName for each employee:
import { Injectable } from '@angular/core'; import { AngularFirestore } from '@angular/fire/compat/firestore'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Employee } from './employee.model'; // Adjust path as needed @Injectable({ providedIn: 'root' }) export class EmployeeService { constructor(private firestore: AngularFirestore) {} getAllEmployees(): Observable<Employee[]> { return this.firestore .collection('users', ref => ref.where('isEmployee', '==', true)) .valueChanges() .pipe( // Transform the array of employees from Firestore map((employees: any[]) => employees.map(employee => ({ ...employee, // Spread all existing properties completeName: `${employee.firstName} ${employee.surname}`, // Optional: Convert Firestore Timestamp to Date if needed birthDate: employee.birthDate?.toDate() || employee.birthDate })) as Employee[] ) ); } }
A quick note: Firestore returns birthDate as a Timestamp object by default, so I added an optional line to convert it to a native Date if that’s what your Employee interface expects.
Step 3: Adding Client-Side Sorting
Since we can’t sort on completeName in the Firestore query, we’ll handle sorting in the component. Here’s how to do it:
In your component:
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Employee } from './employee.model'; import { EmployeeService } from './employee.service'; @Component({ selector: 'app-employee-table', templateUrl: './employee-table.component.html' }) export class EmployeeTableComponent implements OnInit { sortedEmployees$: Observable<Employee[]>; constructor(private employeeService: EmployeeService) {} ngOnInit(): void { this.sortedEmployees$ = this.employeeService.getAllEmployees().pipe( // Create a copy of the array and sort by completeName map(employees => [...employees].sort((a, b) => a.completeName.localeCompare(b.completeName) )) ); } }
Then in your component template, use the async pipe to render the sorted list:
<table> <thead> <tr> <th>Full Name</th> <!-- Other columns --> </tr> </thead> <tbody> <tr *ngFor="let employee of sortedEmployees$ | async"> <td>{{ employee.completeName }}</td> <!-- Render other employee fields --> </tr> </tbody> </table>
Final Recommendation
For your use case (small dataset), the RxJS client-side approach is ideal—it keeps your data clean and avoids sync headaches. If you ever scale to a much larger dataset where client-side sorting becomes slow, you can switch to storing completeName in Firestore, and add logic to update it whenever firstName or surname changes (either via client-side edits or a Firestore Cloud Function trigger).
内容的提问来源于stack exchange,提问作者AndreaNobili

