Angular+Firebase TypeError解决:使用take()获取单条数据报错
问题:使用take()操作符获取Firebase单条数据时出现TypeError
你想通过键从Firebase中获取单个对象,不想实现OnDestroy来手动取消订阅,想用take(1)操作符只获取一次数据就结束订阅,但遇到了错误:TypeError: this.productService.get(...).take is not a function。
你的相关代码如下:
product-form.component.ts
import { Component, OnInit } from '@angular/core'; import { CategoryService } from 'src/app/category.service'; import { ProductService } from 'src/app/product.service'; import { Router, ActivatedRoute } from '@angular/router'; import 'rxjs/add/operator/take'; @Component({ selector: 'app-product-form', templateUrl: './product-form.component.html', styleUrls: ['./product-form.component.css'] }) export class ProductFormComponent implements OnInit { categories$; product = {}; constructor( private router:Router, private route: ActivatedRoute, private categoryService: CategoryService, private productService: ProductService) { this.categories$ = categoryService.getcategories(); let id = this.route.snapshot.paramMap.get('id'); if(id) { this.productService .get(id) .take(1) .subscribe(p => this.product = p) } } save(product){ this.productService.create(product); this.router.navigate(['/admin/products']); } ngOnInit() { } }
product.service.ts
import { Injectable } from '@angular/core'; import { AngularFireDatabase } from 'angularfire2/database'; @Injectable({ providedIn: 'root' }) export class ProductService { constructor(private db: AngularFireDatabase) { } create(product) { return this.db.list('/products').push(product); } getAll() { return this.db.list('/products'); } get(productId){ return this.db.object('/products/' +productId); } }
问题原因分析
咱们先理清核心问题:
- 你的
ProductService.get()方法返回的是AngularFireObject(来自AngularFireDatabase.object()),这并不是一个标准的RxJS Observable,所以它没有take()方法,直接调用肯定会报错。 - 另外,如果你用的是RxJS 6及以上版本,原来的补丁式操作符导入(
import 'rxjs/add/operator/take')已经不再推荐,现在需要用pipe()来组合操作符。
解决方案步骤
1. 修改ProductService,将AngularFireObject转换为Observable
在product.service.ts中,给get()方法加上valueChanges()(或snapshotChanges()),把AngularFireObject转换成RxJS Observable:
import { Injectable } from '@angular/core'; import { AngularFireDatabase } from 'angularfire2/database'; import { Observable } from 'rxjs'; // 可选,显式指定返回类型更规范 @Injectable({ providedIn: 'root' }) export class ProductService { constructor(private db: AngularFireDatabase) { } create(product) { return this.db.list('/products').push(product); } getAll() { return this.db.list('/products'); } get(productId): Observable<any> { // 如果你只需要数据本身,用valueChanges() return this.db.object('/products/' + productId).valueChanges(); // 如果你需要包含key等元数据,用snapshotChanges() // return this.db.object('/products/' + productId).snapshotChanges(); } }
2. 更新组件中的RxJS操作符使用方式
在product-form.component.ts中,修改操作符的导入和调用方式(适配RxJS 6+):
import { Component, OnInit } from '@angular/core'; import { CategoryService } from 'src/app/category.service'; import { ProductService } from 'src/app/product.service'; import { Router, ActivatedRoute } from '@angular/router'; import { take } from 'rxjs/operators'; // 改为导入具体操作符 @Component({ selector: 'app-product-form', templateUrl: './product-form.component.html', styleUrls: ['./product-form.component.css'] }) export class ProductFormComponent implements OnInit { categories$; product = {}; constructor( private router:Router, private route: ActivatedRoute, private categoryService: CategoryService, private productService: ProductService ) { this.categories$ = categoryService.getcategories(); let id = this.route.snapshot.paramMap.get('id'); if(id) { this.productService .get(id) .pipe(take(1)) // 用pipe包裹操作符 .subscribe(p => this.product = p); } } save(product){ this.productService.create(product); this.router.navigate(['/admin/products']); } ngOnInit() { } }
额外说明
valueChanges()会直接返回数据对象,适合只需要数据的场景;snapshotChanges()会返回包含元数据(比如节点key)的快照,如果你需要获取数据的key,就用这个。- 用
take(1)之后,订阅会在获取到第一次数据后自动取消,完美替代手动在ngOnDestroy中取消订阅的操作,符合你的需求。
内容的提问来源于stack exchange,提问作者Mohan Rahatekar
相关产品推荐
相关产品推荐

