Angularfire2. Невозможно прочитать свойство «подписаться» на неопределенное - PullRequest
1 голос
/ 05 марта 2019

Я пытаюсь отделить логику от моих компонентов до службы, она работает, но все равно я получаю консольные ошибки.

Я создаю эту службу и использую ее в своем компоненте

@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)">

Он компилируется без проблем, поскольку вы можете видеть, что он даже правильно отображает впросмотреть, но затем в консоли браузера я получаю это:

Console error

Если я инициализирую бизнес, наблюдаемый в моем сервисе, как это public business: Observable<Business> = new Observable(),Я больше не получаю сообщение об ошибке, но тогда компонент не отображает ничего

Насколько я понимаю, business еще не существует в службе, потому что он либо ожидает подключения businessDoc, либодля его собственного 'valueChanges', поэтому он действительно не определен, когда компонент пытается получить к нему доступ;и именно поэтому его инициализация решает журнал ошибок, но портит представление.

Какой правильный способ сделать это?Спасибо!

Редактировать # 1:

Когда я перемещаю subscribe из конструктора компонента в ngOnInit, он прекращает рендеринг

enter image description here

Редактировать # 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
}

Ответы [ 2 ]

1 голос
/ 05 марта 2019

Проблема в том, что код в том виде, в котором он написан, ожидает, что authState разрешится синхронно.Этот вызов является асинхронным, и, следовательно, вызов состояния, установленного после , который разрешается, вернет undefined (или любое другое значение по умолчанию / начальное значение).

Есть много способов решения этой проблемы, один из которых я выбрал, чтобы добавить initialize метод к службе, который возвращает наблюдаемое, на которое может подписаться компонент.Это разрешится, как только authState разрешит.Я использовал shareReplay, чтобы вызов разрешался только один раз, а затем воспроизводил результат для любых последующих подписок.Оператор tap позволяет устанавливать состояние службы без подписки непосредственно на наблюдаемое.


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;
      });
    });
  }
0 голосов
/ 05 марта 2019

Понятия не имею, что идет не так, но позвольте мне предложить вам другой способ отложить это, может быть, это сработает:

обслуживание:

@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)">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...