角火2。无法读取未定义的 属性 'subscribe'
Angularfire2. Cannot read property 'subscribe' of undefined
我正在尝试将逻辑从我的组件分离到一个服务,它有效但我仍然遇到控制台错误。
我正在构建此服务并在我的组件中使用它
@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)">
它编译没有问题,因为你可以看到它甚至在视图中正确呈现,但是在浏览器控制台中我得到这个:
如果我在我的服务中初始化可观察的业务,就像这样public business: Observable<Business> = new Observable()
,我不会再收到错误,但是组件不会显示任何内容
据我了解,business
尚不存在于服务中,因为它正在等待 businessDoc
连接或等待它自己的 'valueChanges' 所以当组件时它确实未定义尝试访问它;这就是为什么初始化它解决了错误日志,但弄乱了视图。
正确的做法是什么?谢谢!
编辑#1:
当我将 subscribe
从组件构造函数移动到 ngOnInit
时,它停止渲染
编辑#2:
我开始尝试一些事情,包括让我的 Firestore 打开,所以我删除了订阅 authState
的那一行,它开始工作了。这在生产环境中行不通,但我认为问题出在我的授权订阅中,而不是我首先 tough
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
}
不知道出了什么问题,但让我建议您另一种记下它的方法,也许这会奏效:
服务:
@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)">
问题在于编写的代码期望 authState
同步解析。该调用是异步的,因此调用设置为 after 的状态将解析为 return undefined
(或任何 default/initial 值) .
有很多方法可以解决这个问题,我选择的方法是向服务添加一个 initialize
方法,return 是一个可以被组件订阅的可观察对象。这将在 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;
});
});
}
我正在尝试将逻辑从我的组件分离到一个服务,它有效但我仍然遇到控制台错误。
我正在构建此服务并在我的组件中使用它
@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)">
它编译没有问题,因为你可以看到它甚至在视图中正确呈现,但是在浏览器控制台中我得到这个:
如果我在我的服务中初始化可观察的业务,就像这样public business: Observable<Business> = new Observable()
,我不会再收到错误,但是组件不会显示任何内容
据我了解,business
尚不存在于服务中,因为它正在等待 businessDoc
连接或等待它自己的 'valueChanges' 所以当组件时它确实未定义尝试访问它;这就是为什么初始化它解决了错误日志,但弄乱了视图。
正确的做法是什么?谢谢!
编辑#1:
当我将 subscribe
从组件构造函数移动到 ngOnInit
时,它停止渲染
编辑#2:
我开始尝试一些事情,包括让我的 Firestore 打开,所以我删除了订阅 authState
的那一行,它开始工作了。这在生产环境中行不通,但我认为问题出在我的授权订阅中,而不是我首先 tough
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
}
不知道出了什么问题,但让我建议您另一种记下它的方法,也许这会奏效:
服务:
@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)">
问题在于编写的代码期望 authState
同步解析。该调用是异步的,因此调用设置为 after 的状态将解析为 return undefined
(或任何 default/initial 值) .
有很多方法可以解决这个问题,我选择的方法是向服务添加一个 initialize
方法,return 是一个可以被组件订阅的可观察对象。这将在 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;
});
});
}