import {Injectable, inject} from '@angular/core'; import { addDoc, collection, collectionData, CollectionReference, deleteDoc, doc, docData, DocumentData, DocumentReference, Firestore, query, QueryConstraint, setDoc, updateDoc, WithFieldValue, } from '@angular/fire/firestore'; import {Observable} from 'rxjs'; import {map} from 'rxjs/operators'; type CollectionPredicate = string | DbCollection; type DocumentPredicate = string | DbDocument; export class DbCollection { public constructor( private readonly fs: Firestore, private readonly path: string ) {} public add(data: Partial): Promise> { return addDoc(this.ref as CollectionReference, data as WithFieldValue); } public valueChanges(options?: {idField?: string}): Observable { return collectionData(this.ref, options as {idField?: never}) as Observable; } private get ref(): CollectionReference { return collection(this.fs, this.path); } } export class DbDocument { public constructor( private readonly fs: Firestore, private readonly path: string ) {} public set(data: Partial): Promise { return setDoc(this.ref as DocumentReference, data as WithFieldValue); } public update(data: Partial): Promise { return updateDoc(this.ref, data as Partial); } public delete(): Promise { return deleteDoc(this.ref); } public collection(subPath: string): DbCollection { return new DbCollection(this.fs, `${this.path}/${subPath}`); } public valueChanges(options?: {idField?: string}): Observable<(NonNullable & {id?: string}) | undefined> { return docData(this.ref as DocumentReference, options as {idField?: never}) as Observable<(NonNullable & {id?: string}) | undefined>; } private get ref(): DocumentReference { return doc(this.fs, this.path); } } @Injectable({ providedIn: 'root', }) export class DbService { private fs = inject(Firestore); public col(ref: CollectionPredicate): DbCollection { return typeof ref === 'string' ? new DbCollection(this.fs, ref) : ref; } public doc(ref: DocumentPredicate): DbDocument { return typeof ref === 'string' ? new DbDocument(this.fs, ref) : ref; } public doc$(ref: DocumentPredicate): Observable<(NonNullable & {id?: string}) | null> { return this.doc(ref) .valueChanges({idField: 'id'}) .pipe(map(_ => (_ ? _ : null))); } public col$(ref: CollectionPredicate, queryConstraints: QueryConstraint[] = []): Observable { if (typeof ref !== 'string' || queryConstraints.length === 0) { return this.col(ref).valueChanges({idField: 'id'}); } const q = query(collection(this.fs, ref), ...queryConstraints); return collectionData(q, {idField: 'id'}) as Observable; } }