【问题标题】:Angular firestore app queries with userId not working使用 userId 的 Angular Firestore 应用程序查询不起作用
【发布时间】:2019-04-13 07:10:17
【问题描述】:

我有一个带有 firebase 应用程序的 angular 6 我有一个带有可观察对象的服务(我稍后希望多个订阅者收听同一个可观察对象) 还有一个订阅这个 observable 的组件 我试图实现的是获取 auth 对象,从中获取 userId (UID) 并在 db 中搜索具有此 id 的所有文档

组件在 ngOnInit 钩子上调用 observable,但在阶段它仍然为空,所以我得到 ​​p>

无法读取未定义的属性“订阅”

问题是,如果我运行代码来填充订阅块内的 observable(以获取身份验证数据),我看不到数据,如果它在它之外(使用静态 UID 运行同步)比它确实填写正确并显示数据 这是服务的代码

import { Injectable, EventEmitter } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, DocumentChangeAction } from '@angular/fire/firestore';
import { Expense } from '../model/Expense';
import { Observable, ReplaySubject, Subject, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from '../core/services/auth.service';

@Injectable({
    providedIn: 'root',
})
export class ExpenseService {

    private dbPath = '/expenses';

    expenseDetailEvent: EventEmitter<Expense> = new EventEmitter<Expense>();

    expensesRef: AngularFirestoreCollection<Expense> = null;
    expenses$: Observable<Expense[]>;

    constructor(private db: AngularFirestore, auth: AuthService) {
        auth.user$.subscribe(
            user => {
                this.expensesRef = this.db.collection<Expense>(this.dbPath, ref =>
                    ref.where('clientId', '==', user.uid)
                );
                this.expenses$ = this.expensesRef.snapshotChanges().pipe(
                    map(actions =>
                        actions.map(a => {
                            const data = a.payload.doc.data() as Expense;
                            const id = a.payload.doc.id;
                            return { id, ...data };
                        })
                    )
                );
            },
            error => console.log(error)
        );
    }

    createExpense(expense: Expense): Observable<any> {
        return from(this.expensesRef.add(expense));
    }

    updateExpense(expense: Expense): Observable<any> {
        return from(this.expensesRef.doc(expense.id).update(expense));
    }
}

组件只是这样做

ngOnInit() {
        this.expenseService.expenses$.subscribe(expenses => this.expenses = expenses);
    }

我注意到网上所有的例子都在组件上调用了这个,但我觉得数据库调用应该在一个服务中,就像它使用普通的 http api 调用一样

更新 如果我像这样初始化服务

expenses$: Observable<Expense[]> = of([]);

所以应用程序当然不会崩溃,但我没有看到任何数据 如果我只是将代码拉到外面并使用静态 UID...(当然 user.uid 与我粘贴的静态字符串相同)

这是更新的代码

constructor(private db: AngularFirestore, auth: AuthService) {
    console.log('being here ,i see the data on screen using the static uid');
    this.expensesRef = this.db.collection<Expense>(this.dbPath, ref =>
        ref.where('clientId', '==', 'noYVScDtKfNB5aFqRkAJR7ghxio2')
    );
    this.expenses$ = this.expensesRef.snapshotChanges().pipe(
        map(actions =>
            actions.map(a => {
                const data = a.payload.doc.data() as Expense;
                const id = a.payload.doc.id;
                return { id, ...data };
            })
        )
    );
    auth.user$.subscribe(
        user => {
            console.log('if inside here than it will not show any data on screen');

        },
        error => console.log(error)
    );
}

【问题讨论】:

  • 你有没有尝试使用ngAfterViewInit()
  • 我做了,没有效果,问题是observable在那之后被初始化了

标签: angular firebase google-cloud-firestore angular6


【解决方案1】:

问题是 expenses$ Observable 将仅在 auth.user$ 发出某些内容后定义,因为那时它被分配了。

解决方案 1 您可以尝试在更高级别组件的构造函数中注入ExpenseService 服务,甚至可能是AppComponent,以便调用ExpenseService 的构造函数并分配expenses$ Observable(您只需要注入它,没有任何方法调用或属性访问)。此时,当您加载组件时,应该定义 Observable

解决方案 2 使用 of 手动创建 Observable expenses$ 然后订阅 this.expensesRef.snapshotChanges() 您可以在 expenses$ 中返回值

类似:

...

expensesSubject = new Subject<Expense[]>();
expenses$: from(expensesSubject);

constructor(private db: AngularFirestore, auth: AuthService) {
    auth.user$.subscribe(
        user => {
            this.expensesRef = this.db.collection<Expense>(this.dbPath, ref =>
                ref.where('clientId', '==', user.uid)
            );
            this.expensesRef.snapshotChanges().pipe(
                map(actions =>
                    actions.map(a => {
                        const data = a.payload.doc.data() as Expense;
                        const id = a.payload.doc.id;
                        return { id, ...data };
                    })
                )
            ).subscribe((whatever) => {
              expensesSubject.next(whatever);
            })
        }
    )
}

【讨论】:

  • 你能详细说明一下解决方案2
  • 我编辑了答案,可能需要检查打字,但你明白了。在组件中保持一切原样。
  • 好极了,它起作用了,但我不太明白为什么在它不起作用之前以及是否有必要同时使用两者(费用 $ 和费用主题)
  • 不是必需的,但一种很好的做法。通常您希望将expensesSubject 标记为服务专用,然后将expenses$ 标记为公开。这样可以避免组件或其他服务能够在流中发出数据。
猜你喜欢
  • 1970-01-01
  • 2018-12-28
  • 2020-07-27
  • 2021-04-23
  • 1970-01-01
  • 2020-01-15
  • 1970-01-01
  • 2018-03-18
  • 2019-02-05
相关资源
最近更新 更多