角度可观察未定义,尽管在构造函数中设置并正确订阅(使用 Firebase)



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

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

我的组件正在订阅我的品牌服务中的"当前品牌"可观察:

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(( 方法内的构造器中,该方法在解决承诺时执行。我似乎无法弄清楚这个...

你可以currentBrand做一个RxJSBehaviorSubject。这是混合承诺和可观察量的常见情况。我正在使用 RxJSfrom将承诺转换为可观察量,并使用运算符映射它switchMap最终在this.getBrand()方法中进行(大概是 HTTP(调用。

尝试以下操作

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;
}
});
}

因此,您面临的问题是因为您的代码异步执行。我试图内联解释这个问题。

//When the instance of ur service get created this thing is undefined 
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 is async function which will take some time
this.auth.currentUser.then((authUser) => {
console.log('this is logged prior to the undefined error');
// this again is async operation
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');
// ONCE ALL THE ASYNC OPERATION ARE DONE YOU ARE ASSINGING THE VALUE HERE
this.currentBrand = this.getBrand(user.defaultBrandId);
});
});
}
// SO it will take some time to set value 
//Till that time currentBrand will be undefined
getCurrentBrand(): Observable<Brand> {
return this.currentBrand;
}

在您的组件内部,您正在尝试在 ngoninit 方法中获取其值,如下所示 私人当前品牌:品牌;

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

this.brandService.getCurrentBrand() //此值将未定义,直到您的所有承诺得到解决。

我希望你明白了问题。现在的解决方法可能是将当前品牌作为行为主体,然后进行下一步的内在承诺。如下所示

currentBrand : =new BehaviourSubject(initial value);

在你的承诺中,这样做。

currentBrand.next('value you get from ur promise' )

最新更新