Reputation: 1
I am trying to retrieve data from the /users
list and getting this error:
Type 'Subscription' is not assignable to type 'FirebaseListObservable'. Property '$ref' is missing in type 'Subscription'.
below is my code:
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);
});
}
}
Upvotes: 0
Views: 2554
Reputation:
Your code is rife with anti-patterns and unnecessary parts.
First, don't use preserveSnapshot
. You don't need to with AngularFire. You can subscribe directly to the array itself that is emitted by the FirebaseListObservable
. Once you have it, there is no need to iterate over it and push its elements onto some other array--you already have the array, which you can use as is, or map, or store (but see below), or whatever. So just write:
this.user = this.afDb.list('/users', {
query: {
orderByChild: 'email',
equalTo: this.email,
}})
}).subscribe(users => {
console.log("users are", users);
});
But there's still a problem here. this.user
is not what you might thing: it will be the subscription returned by subscribe
. You can't do anything with it other than unsubscribe. If you want to "unwrap" the list of users and store it as a local static property, you need to assign it in the subscribe
handler, where it's available:
.subscribe(users => {
console.log("users are", users);
this.users = users;
})
However, unwrapping the observable and storing it in a static property is (often) an anti-pattern. The problem is that you have no idea when it's going to be ready. In the best case, you'll have to wrap places in your template where it's used with *ngIf="users"
, or qualify references to it in the template with the existential operator ?
as in users?.length
(which is not AoT-friendly).
Instead, leave the observable as is, and dereference it directly in your template using the async
pipe:
this.users$ = this.afDb.list('/users', {
query: {
orderByChild: 'email',
equalTo: this.email,
}});
<div *ngFor="let user of users$ | async">
User is {{user.name}}
</div>
Here I'm following the convention of suffixing properties representing observables with the $
, which is useful to maintain your sanity.
If you only want the names, then you can map the observable itself to create an observable of arrays of names:
this.names$ = this.users$.map(users => users.map(user => user.name));
Note that these two map
calls are different. The first is mapping the observable into a new observable. The second is mapping the array of users emitted by the observable into a new array containing the names. Now in your template:
<li *ngFor="let name of names$">{{name}}</li>
The discussion above relates to components. In your case, you have a service. In general, it is an anti-pattern for services to subscribe to some observable and store the value in a static local property. The problem is that components referencing that property on the service will not be updated. Instead, your service should expose observables, and let the consumer--usually a component--dereference/unwrap them.
To find users whose mobile numbers match, you can still stay in the world of observables:
this.matchingUsers$ = this.users$.map(users => users.filter(user => user.mobile === mobile));
which will create an observable of arrays of users with matching mobile numbers, which will empty if none match. Some consuming template could use this as
<div *ngFor="let matchingUser of userService.matchingUsers$ | async">
Found matching user {{user.name}}
</div>
or
<div *ngIf="userService.matchingUsers$ | async as matchingUsers">
<div *ngIf="!matchingUsers.length">
Sorry, no matching users.
</div>
</div>
and so on. Note that this as
syntax requires Angular 4.
That's the Angular/observable/AngularFirebase way.
Upvotes: 1
Reputation: 29614
subscribe
function returns a Subscription object.
You are assigning to a FirebaseListObservable
reference.
Instead subscribe after assigning if you need to reuse the observable.
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());
});
Upvotes: 0