I have method in my service, inside which I am trying to return an Observable of the User object. Here is the my code -
constructor(private firestore: AngularFirestore,
private db: AngularFireDatabase) { }
/**
* get user with given email, if not return null
*
* @param email email to fetch
*/
getUserByEmail(email: string): Observable<User> {
var user: User;
let userRef = this.firestore.collection("users").ref.where('email', '==', email);
userRef.get().then(res => res.forEach(userDoc => {
user = userDoc[0].data() as User; // since email IDs are unique, I want the 0th element.
user.id = userDoc[0].id;
console.log(user); // has the data
// return user; // doesn't work, not the right thing
}));
console.log(user); // undefined, since call is async
return of(user);
}
Inside the component, I want that data for the next steps, so I did this -
checkEmail() {
var user$: User
this.us.getUserByEmail(this.existingUserForm.value.email).subscribe(user => {
console.log(user);
if (user) {
this.msg$ = "success";
// next steps
}
else {
this.msg$ = "User with this email does not exist!";
}
});
}
I am not sure how to return an observable from my service, so that I can use the data in my component. And is this the right way to do what I am trying to?
If you want an observable from Firestore, you need to return the .valueChanges() on a AngularFirestoreCollection. Read this doc for reference: https://github.com/angular/angularfire2/blob/master/docs/firestore/querying-collections.md.
In your service do: (I've declared the collection and user$ variables for clarity).
getUserByEmail(email: string): Observable<User> {
const collection = this.firestore.collection<User>('users', ref => ref.where('email', '==', email))
const user$ = collection
.valueChanges()
.pipe(
map(users => {
const user = users[0];
console.log(user);
return user;
})
);
return user$;
}
If you want the ID as well on your user you need to use snapshotChanges(). Sometimes it's easier to maintain the id on the user data when saving to firestore to avoid using snapshotChanges (in my opinion).
// Query the users by a specific email and return the first User with ID added
return this.firestore.collection<User>('users', ref => ref.where('email',
'==', email))
.snapshotChanges()
.pipe(map(users => {
const user = users[0];
if (user) {
const data = user.payload.doc.data() as User;
const id = user.payload.doc.id;
return { id, ...data };
}
else {
return null;
}
}));
In your Component code you can call this by
checkEmail() {
this.user$ = this.us.getUserByEmail('[email protected]')
.pipe(
tap(user => {
if (user) {
this.msg = 'success';
} else {
this.msg = 'User with this email does not exist!';
}
}));
}
Note that the user will be null until you call subscribe on this.user$ OR in your html use the async pipe which handles subscribe and unsubscribe on a observable.
<div *ngIf="(user$ | async) as user">{{ user.email }}</div>
Try to avoid using the async pipe on the same observable multiple times in your html, and instead set it as a local html variable which i do above (as user) to avoid unnecessary data trips to the db
A note on naming conventions. Observables' variable names are often suffixed with '$', while other (string, numbers etc) don't have the '$'.
Create a service:
constructor(private db: AngularFirestore) { }
getCategories() {
return this.db.collection('categories').valueChanges();
}
use it:
categories$;
constructor(categoryService: CategoryService) {
this.categories$ = categoryService.getCategories();
}
display your documents:
<select id="category" type="text" class="form-control">
<option value=""></option>
<option *ngFor="let c of categories$ | async" [value]="c.$key">
{{ c.name }}
</option>
</select>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With