首页 > 解决方案 > 尽管在构造函数中设置并正确订阅了Angular Observable undefined(使用firebase)

问题描述

我的组件错误ERROR TypeError: "this.brandService.getCurrentBrand(...) is undefined"

问题是this.auth.currentUser.then()BrandService 构造函数块内的代码在组件订阅它之前没有运行,因此组件出现未定义的错误。

我的组件订阅了我的 BrandService 中的“currentBrand” Observable:

  private currentBrand: Brand;

  ngOnInit(): void {
    this.brandService.getCurrentBrand().subscribe((brand) => {
      this.currentBrand = brand;
    });
  }

BrandService在构造函数中设置“currentBrand”,并使用 getCurrentBrand() 方法返回它:

  private currentBrand: Observable<Brand>;

  constructor(
    private afs: AngularFirestore,
    private auth: AngularFireAuth,
    private userService: UserService
  ) {
    this.brandsCollection = this.afs.collection('brands', (ref) =>
      ref.orderBy('name', 'asc')
    );

    this.auth.currentUser.then((authUser) => {
      console.log('this is logged prior to the undefined error');

      this.userService.getUserByEmail(authUser.email).then((user) => {
        console.log('for some reason, this is logged after the undefined error, need this to run prior to in order to eliminate the error');
        this.currentBrand = this.getBrand(user.defaultBrandId);
      });
    });
  }

  getCurrentBrand(): Observable<Brand> {
    return this.currentBrand;
  }

  getBrand(id: string): Observable<Brand> {
    this.brandDoc = this.afs.doc<Brand>(`brands/${id}`);

    this.brand = this.brandDoc.snapshotChanges().pipe(
      map((action) => {
        if (action.payload.exists === false) {
          return null;
        } else {
          const data = action.payload.data() as Brand;
          data.id = action.payload.id;
          return data;
        }
      })
    );

    return this.brand;
  }

我不确定我在这里做错了什么。我认为 BrandService 构造函数中的所有内容都应该在组件的 ngOnInit 之前运行,因此不应该发生这个未定义的错误。我正确地将代码放置在 .then() 方法中的构造函数中,该方法在解决 Promises 时执行。我只是似乎无法弄清楚这一点...

标签: angularfirebasepromiseobservable

解决方案


你可以制作currentBrand一个 RxJS BehaviorSubject。这是混合 Promise 和 observables 的常见情况。我正在使用 RxJS将 Promisefrom转换为 observables 并使用switchMap运算符对其进行映射,以最终在方法中进行(可能是 HTTP)调用this.getBrand()

尝试以下

private currentBrand = new BehaviorSubject<Brand>(null);

constructor(
  private afs: AngularFirestore,
  private auth: AngularFireAuth,
  private userService: UserService
) {
  this.brandsCollection = this.afs.collection('brands', (ref) =>
    ref.orderBy('name', 'asc')
  );

  from(this.auth.currentUser).pipe(
    switchMap((authUser) => {
      console.log('this is logged prior to the undefined error');
      return from(this.userService.getUserByEmail(authUser.email)).pipe(
        switchMap((user) => {
          console.log('for some reason, this is logged after the undefined error, need this to run prior to in order to eliminate the error');
          return this.getBrand(user.defaultBrandId);
        })
      )
    })
  ).subscribe(
    response => {
      this.setCurrentBrand(response);      // <-- push the response to `currentBrand` observable here
    },
    error => {
      // handle error
    }
  );
}

getCurrentBrand(): Observable<Brand> {
  return this.currentBrand.asObservable();
}

setCurrentBrand(brand: Brand) {
  this.currentBrand.next(brand);
}

零件

ngOnInit(): void {
  this.brandService.getCurrentBrand().subscribe((brand) => {
    if (brand) {
      this.currentBrand = brand;
    }
  });
}

推荐阅读