【问题标题】:why am i getting this error:Type Subscription is not assignable to type FirebaseListObservable<any>?为什么我会收到此错误:类型订阅不可分配给类型 FirebaseListObservable<any>?
【发布时间】:2017-11-23 19:43:55
【问题描述】:

我正在尝试从/users 列表中检索数据并收到此错误:

类型“订阅”不可分配给类型 'FirebaseListObservable'。类型中缺少属性“$ref” '订阅'。

下面是我的代码:

import { Injectable } from '@angular/core';

import { AngularFireAuth } from 'angularfire2/auth';
import firebase from 'firebase';
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable  } from 'angularfire2/database';



@Injectable()
export class FbauthserviceProvider {

  user : FirebaseListObservable<any>;   
  email: string;

  constructor(public afAuth: AngularFireAuth, public afDb: AngularFireDatabase) {
    console.log('Hello FbauthserviceProvider Provider');
  }

  signup_with_username(email: string,password: string){
    this.email = email;

    this.user = this.afDb.list('/users', {
      query: {
        orderByChild: 'email',
        equalTo: this.email,
      },
      preserveSnapshot: true
    }).subscribe(snapshots => {
      let user_data = [];
      snapshots.forEach(snapshot => {
        user_data.push(snapshot.val());
      });
      console.log(user_data);
    });
  }
}

【问题讨论】:

    标签: angular ionic2 angularfire2


    【解决方案1】:

    您的代码充满了反模式和不必要的部分。

    首先,不要使用preserveSnapshot。您不需要使用 AngularFire。您可以直接订阅由FirebaseListObservable 发出的数组本身。一旦你有了它,就不需要遍历它并将它的元素推送到其他数组上——你已经有了数组,你可以按原样使用它,或者映射,或者存储(但见下文),或者其他什么。所以就写吧:

    this.user = this.afDb.list('/users', {
      query: {
        orderByChild: 'email',
        equalTo: this.email,
      }})
    }).subscribe(users => {
      console.log("users are", users);
    });
    

    但是这里还有一个问题。 this.user 不是你想的那样:它将是subscribe 返回的订阅。除了取消订阅之外,您无法对其进行任何操作。如果要“解包”用户列表并将其存储为本地静态属性,则需要将其分配到可用的 subscribe 处理程序中:

    .subscribe(users => {
      console.log("users are", users);
      this.users = users;
    })
    

    然而,展开 observable 并将其存储在静态属性中(通常)是一种反模式。问题是你不知道它什么时候准备好。在最好的情况下,您必须将模板中与*ngIf="users" 一起使用的位置包装起来,或者在模板中使用存在运算符? 限定对它的引用,如users?.length(这不是AoT 友好的) )。

    相反,让 observable 保持原样,并使用 async 管道直接在模板中取消引用它:

    this.users$ = this.afDb.list('/users', {
      query: {
        orderByChild: 'email',
        equalTo: this.email,
      }});
    
    <div *ngFor="let user of users$ | async">
      User is {{user.name}}
    </div>
    

    这里我遵循使用 $ 为表示可观察对象的属性添加后缀的约定,这有助于保持理智。

    如果您只想要名称,那么您可以映射 observable 本身以创建名称数组的 observable:

    this.names$ = this.users$.map(users => users.map(user => user.name));
    

    请注意,这两个map 调用是不同的。第一个是将 observable 映射到一个新的 observable 中。第二个是将 observable emitted 的用户数组映射到包含名称的新数组中。现在在您的模板中:

    <li *ngFor="let name of names$">{{name}}</li>
    

    上面的讨论与组件有关。就您而言,您有一项服务。一般来说,服务订阅一些可观察对象并将值存储在静态本地属性中是一种反模式。问题是在服务上引用该属性的组件不会被更新。相反,您的服务应该公开可观察对象,并让使用者(通常是组件)取消引用/解包它们。

    要找到手机号码匹配的用户,您仍然可以留在 observables 的世界中:

    this.matchingUsers$ = this.users$.map(users => users.filter(user => user.mobile === mobile));
    

    这将创建一个可观察的具有匹配手机号码的用户数组,如果没有匹配则为空。一些消费模板可以将其用作

    <div *ngFor="let matchingUser of userService.matchingUsers$ | async">
      Found matching user {{user.name}}
    </div>
    

    <div *ngIf="userService.matchingUsers$ | async as matchingUsers">
      <div *ngIf="!matchingUsers.length">
        Sorry, no matching users.
      </div>
    </div>
    

    等等。请注意,此 as 语法需要 Angular 4。

    这就是 Angular/observable/AngularFirebase 的方式。

    【讨论】:

    • Thxxx @torazaburo 详细解释。我正在检索的此用户数据不是来自身份验证用户部分。它只是来自我的数据库,我将用户个人资料信息存储在“/用户”中。我需要对用户号码进行一些检查。因此,我必须获取该用户数据并打开包装以检查手机号码是否匹配。
    【解决方案2】:

    subscribe 函数返回一个订阅对象。

    您正在分配给FirebaseListObservable 引用。 如果您需要重用可观察对象,请在分配后订阅。

    this.user = this.afDb.list('/users',{
    
        query: {
    
            orderByChild: 'email',
            equalTo: this.email,
        },
        preserveSnapshot: true
    })
    this.user.subscribe(snapshots =>{
    
            let user_data = [];
            snapshots.forEach(snapshot=>{
                user_data.push(snapshot.val());
            });
    

    【讨论】:

    • 谢谢@suraj 的答案。我得到 snapshot.val() 不是函数错误
    • 取决于snapshot 是什么?你可以记录吗?你将不得不调试一下
    • 实际上通过执行 snapshots.forEach(snapshot =>{ console.log(snapshot.email) } 我得到了价值!!谢谢@suraj。但唯一的问题是我想发送用户数据(快照)到数组。
    • 那么直接pushsnapshot还是直接把snapshots数组赋值给user_data?
    • 因为快照不仅显示对象数组中的数据,它还显示其他一些东西,如 $exists、$function() 和其他不需要的东西..所以当我将它推送到数组时我仍然得到相同的数据对象+其他东西
    猜你喜欢
    • 2019-03-27
    • 2018-04-21
    • 2018-10-12
    • 2019-08-01
    • 2021-07-30
    • 2016-11-12
    • 1970-01-01
    • 2021-02-06
    • 1970-01-01
    相关资源
    最近更新 更多