【问题标题】:Angularfire2. Cannot read property 'subscribe' of undefined角火2。无法读取未定义的属性“订阅”
【发布时间】:2019-03-05 23:35:03
【问题描述】:

我正在尝试将逻辑从我的组件分离到服务中,它可以工作,但我仍然遇到控制台错误。

我正在构建这个服务并在我的组件中使用它

@Injectable({
  providedIn: 'root'
})
export class DatabaseService {
  public businessDoc: AngularFirestoreDocument<Business>;
  public business: Observable<Business>;

  constructor(private auth: AngularFireAuth, private db: AngularFirestore) {
    this.auth.authState.subscribe(user => {
      this.uid = user.uid;
      this.businessDoc = this.db.doc<Business>(this.businessAddress);
      this.business = this.businessDoc.valueChanges()
    }
  }
  private get businessAddress() {
    return 'users/' + this.uid
  }
}

这是我的组件

export class ConfigComponent implements OnInit {

  config: Config;

  constructor(public db: DatabaseService) {
    this.db.business.subscribe(res => {
      this.config = res.config;
    });
  }

最后,在我的模板中

<input [ngModel]="config?.regular" (ngModelChange)="onConfigChange($event)">

它编译没有问题,你可以看到它甚至在视图中正确呈现,但是在浏览器控制台中我得到了这个:

如果我在我的服务中初始化业务 observable,例如 public business: Observable&lt;Business&gt; = new Observable(),我不会再收到错误消息,但组件不会显示任何内容

据我了解,business 尚不存在于服务中,因为它要么等待businessDoc 连接,要么等待它自己的“valueChanges”,因此当组件尝试访问它时它确实是未定义的;这就是为什么初始化它可以解决错误日志,但会弄乱视图。

这样做的正确方法是什么?谢谢!

编辑#1:

当我将 subscribe 从组件构造函数移动到 ngOnInit 时,它会停止渲染

编辑#2:

我开始尝试一些事情,包括打开我的 Firestore,所以我删除了我订阅 authState 的行,它开始工作了。这在生产中不起作用,但我认为问题出在我的身份验证订阅上,而不是我第一次遇到困难的地方

constructor(private auth: AngularFireAuth, private db: AngularFirestore) {
// this.auth.authState.subscribe(user => {
    this.uid = "twCTRUpXvYRiYGknXn6j7fe0mvj2";
    this.businessDoc = db.doc<Business>(this.businessAddress);
    this.business = this.businessDoc.valueChanges()
 // });
}

private get businessAddress() {
    return 'users/' + this.uid
}

【问题讨论】:

  • 我已经删除了关于您的编辑的回答 :)
  • 对于一件事,您应该将ConfigComponent 的构造函数中的逻辑移动到方法ngOnInit,这就是它所属的地方。问题似乎是this.businessDoc.valueChanges(); 没有返回一个可观察的。在该行之前/之后放置一个断点并检查返回值。
  • @igor 奇怪的是,当我将其移至 ngOnInit 时,它会停止渲染,并且出现相同的错误
  • 您的DatabaseService 上有@Injectable() 吗?
  • @PierreDuc 是的,我愿意。我有@Injectable({ providedIn: 'root'})

标签: angular angularfire2


【解决方案1】:

问题在于编写的代码期望authState 同步解析。该调用是异步的,因此对设置为 after 的状态的调用将返回 undefined(或任何默认/初始值)。

有很多方法可以解决这个问题,我选择的一种是在服务中添加一个initialize 方法,该方法返回一个组件可以订阅的可观察对象。这将在 authState 解决后解决。我使用了shareReplay,这样调用只会成功解决一次,然后将结果重播给任何后续订阅。 tap 运算符允许在服务上设置状态,而无需直接订阅 observable。


DatabaseService.ts

import { tap, shareReplay } from 'rxjs/operators';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DatabaseService {
  public businessDoc: AngularFirestoreDocument<Business>;
  public business: Observable<Business>;
  private readonly _initialize: Observable<any>;

  constructor(private auth: AngularFireAuth, private db: AngularFirestore) {
    this._initialize = this.auth.authState.pipe(tap(user => {
        this.uid = user.uid;
        this.businessDoc = this.db.doc<Business>(this.businessAddress);
        this.business = this.businessDoc.valueChanges()
    }), shareReplay());
  }

  initialize() : Observable<any> {
    return this._initialize;
  }

  private get businessAddress() {
    return 'users/' + this.uid
  }
}

ConfigComponent.ts

export class ConfigComponent implements OnInit {

  config: Config;

  constructor(private readonly db: DatabaseService){ }

  ngOnInit() {
    this.db.initialize().subscribe(() => {
      this.db.business.subscribe(res => {
        this.config = res.config;
      });
    });
  }

【讨论】:

  • 这成功了!谢谢,但是能详细点吗?我的印象是订阅authState 会异步解决。或者有什么其他方法可以解决这个问题?
  • @Darkade - 在您的代码中,它确实是异步解析的,但是 之后,ConfigComponent(以及 ngOnInit 调用)的构造函数被执行,这意味着在ConfigComponent 访问它的那一刻没有设置服务的状态。 “新”代码允许组件等待正在初始化的状态(即来自 authstate 的回调发生)。
【解决方案2】:

不知道出了什么问题,但让我建议你另一种方式来放下它,也许这会奏效:

服务:

@Injectable({
  providedIn: 'root'
})
export class DatabaseService {
  private businessAddress: string = "business"

  public businessDoc = this.af.doc<Business>(this.businessAddress);
  public business = this.businessDoc.valueChanges();

  constructor(readonly af: AngularFirestore) {}
}

组件:

export class ConfigComponent implements OnInit {

  config$: Observable<Config> = this.db.business.pipe(
    map((res) => res && res.config)
  );

  constructor(readonly db: DatabaseService) {}
}

模板:

<input [ngModel]="(config$ | async)?.regular" (ngModelChange)="onConfigChange($event)">

【讨论】:

  • 谢谢。我试过这个,我意识到我忽略了在我的问题中提出导致这种行为的东西。我也在做一些用户身份验证。当我从身份验证中删除我的订阅时,我不再看到错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多