Getting an Observable for data retrieved from a Firestore query in Angular 7

One of the methods in my service aims to return an Observable of the User object. Below is the code snippet I have written:

 constructor(private firestore: AngularFirestore,
    private db: AngularFireDatabase) { }

 /**
   * Retrieve user based on provided email address, return null if not found
   * 
   * @param email The email to search for
   */
  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;
      user.id = userDoc[0].id;

      console.log(user);

    }));

    console.log(user);

    return of(user);
  }

In my component, I require this data for subsequent actions. Therefore, I implemented the following:

checkEmail() {

    var user$: User

    this.us.getUserByEmail(this.existingUserForm.value.email).subscribe(user => {

      console.log(user);

      if (user) {

          this.msg$ = "success";
          // proceed with next steps
      }
      else {
        this.msg$ = "User with this email does not exist!";
      }

    });

  }

I am uncertain about how to properly return an observable from the service in order to utilize the data within my component. Additionally, I question the correctness of the approach I am taking. Any suggestions would be greatly appreciated.

Answer №1

If you're looking to retrieve data from Firestore in an observable format, you'll need to use .valueChanges() on an AngularFirestoreCollection. For detailed information, refer to this documentation: https://github.com/angular/angularfire2/blob/master/docs/firestore/querying-collections.md.

In your service implementation, make sure to follow these steps (I've included 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 also require the ID along with the user's data, using snapshotChanges() is recommended. It can be advantageous to store IDs as part of user data when saving to Firestore to avoid using snapshotChanges (in my opinion).

// Query users based on email and return the first User with added ID    
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 invoke this method as shown below

checkEmail() {
this.user$ = this.us.getUserByEmail('<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9efbf3fff7f2def9f3fff7f2b0fdf1f3">[email protected]</a>')
  .pipe(
    tap(user => {
      if (user) {
        this.msg = 'success';
      } else {
        this.msg = 'User with this email does not exist!';
      }
    }));
}

Note that the user object will be null until you subscribe to this.user$ OR utilize the async pipe in your HTML which manages subscription and unsubscription of observables.

<div *ngIf="(user$ | async) as user">{{ user.email }}</div>

It's advisable to avoid using the async pipe multiple times on the same observable in your HTML. Instead, assign it to a local variable (as done above with 'user') to prevent unnecessary database calls.

A quick tip on naming conventions: observeable variable names typically end with '$', while other types (strings, numbers, etc.) do not include the '$' suffix.

Answer №2

Establish a new service:

   constructor(private database: AngularFirestore) { }

      getCategories() {
        return this.database.collection('categories').valueChanges();
      }

Utilize the service:

 categories$;

  constructor(categoryService: CategoryService) {
    this.categories$ = categoryService.getCategories();
  }

Showcase your data:

 <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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The 'React' namespace does not contain the exported members 'ConsumerProps' or 'ProviderProps'

Is it possible to install this library in Visual Studio with React version 15.0.35? Are there any other libraries that are compatible with this specific React version? import * as React from 'react'; import { RouteComponentProps, NavLink } from ...

Issue detected in rxjs-compat operator's shareReplay file at line 2, column 10:

I've encountered an issue with the angular material spinner I'm using in my project. The error message is as follows: ERROR in node_modules/rxjs-compat/operator/shareReplay.d.ts(2,10): error TS2305: Module '"D:/ControlCenter/ofservices ...

I am encountering issues with the TypeScript repository build on my local machine, but it successfully passes when

I am encountering an issue with a TypeScript repository failing to build on my local machine. The error message I receive is as follows: $ tsc --pretty -p tsconfig.json ../../../../../../node_modules/@types/graphql/subscription/subscribe.d.ts:17:12 - erro ...

Setting up an OR guard in NestJS is a crucial step in managing

I need to secure a controller route using Guards, including IsAuthentifiedGuard, HasRoleGuard, and IsSafeGuard. I want the route to be accessible if at least one of these conditions is met: IsAuthentifiedGuard and HasRoleGuard pass IsSafeGuard passes For ...

How to Dynamically Load SVGs in Angular 10 Without Using IMG or OBJECT Elements

I have been exploring a more streamlined method for loading SVG files without relying on IMG or OBJECT tags, as it limits my ability to control fill colors through external CSS. While using inline SVG seems like the best option, managing numerous component ...

What is the best way to adjust the padding within a mat-expansion-panel-body?

I recently created an expansion panel that is working well, but I am having trouble removing a padding from the subpanel section. Despite my efforts, I haven't been able to find a suitable solution. Here's a link to the panel image: https://i.ss ...

Steer clear of chaining multiple subscriptions in RXJS to improve code

I have some code that I am trying to optimize: someService.subscribeToChanges().subscribe(value => { const newValue = someArray.find(val => val.id === value.id) if (newValue) { if (value.status === 'someStatus') { ...

Changing the Month Label Format from Short (MMM) to Long (MMMM) in Angular Material Datepicker

I am looking to customize the month labels in Angular Material datepicker. By default, the month view displays in MMM format and I want to change it to MMMM using custom MatDateFormats. export const APP_DATE_FORMATS: MatDateFormats = { parse: { dat ...

What triggers the firing of onAuthStateChanged() in a Netxjs application?

Hey everyone, I've encountered an issue with a useEffect hook in my root page.tsx file within a Next.js app. Specifically, on my /SignIn page.tsx, I've set up Google as a login provider using FirebaseAuth. When I log in with signInWithPopup, I ex ...

What is the process for incorporating the 'url-regex' npm package into an Angular(2/4) project?

I'm currently working on a project with Angular 4 and I've run into some issues while trying to use the url-regex package within my Component. After some troubleshooting, I discovered that this approach seems to work: import * as urlRegex from ...

Tips for testing the window.innerWidth property in Angular 8?

I am relatively new to Angular and TDD. Right now, I am attempting to test a function that is called during a resize event. Below is the code snippet in question: header.component.ts @Component({ selector: 'app-header', templateUrl: &ap ...

What is the proper way to utilize the ES6 import feature when using a symbolic path to reference the source file?

I am seeking a deeper understanding of the ES6 import function and could use some assistance. The Situation Imagine that I have a portion of code in my application that is frequently used, so I organize all of it into a folder for convenience. Now, in t ...

Expanding Classes through Index signatories

My attempt at creating an abstract class is not going as smoothly as I hoped. I suspect my limited knowledge of TypeScript is the primary issue, even though this seems like a common scenario. The abstract class I'm working on is called Program. It co ...

Strategies for Populating Objects in Angular 2

I have a created a complex class hierarchy with multiple classes. I need assistance with populating the "OptionsAutocomplete" object in angular2. Can someone please provide guidance on how to achieve this? interface IOpcionesAutocomplete { opciones ...

Ways to remove newly added tasks using JavaScript function?

I have an existing list of li elements in my HTML that can be deleted using JavaScript. However, whenever I add a new li, the delete function no longer works on the newly added item. I suspect the issue lies within the current implementation of the for loo ...

App-Root in Angular 2 not loading properly in ExpressJS route

As a newcomer to NodeJS and Express, I am trying to create a simple '/' route that points to Angular's default index.html file located at client/src/index.html. This file contains the app-root tag. While the '/' route successfully ...

Using Angular to populate textboxes with SQL data

I am encountering an issue with a mat-table that retrieves its data from a database. One of the columns needs to be editable by the user so that they can update the content and reflect changes in the database. The problem lies in loading the data into the ...

Exploring the zoom functionality and tooltip features of Area Range charts in Highcharts

I need help with a couple of things on the high charts area range chart: I am trying to enable zooming into the y-axis of the chart, but I am facing difficulties. Can anyone suggest how this can be achieved? Below is the link to my jsfiddle with the sam ...

What does the "xxx" parameter represent in the ng g universal xxx command when using AngularCLI 6+?

In this scenario, what is the purpose of morningharwood-server? Can we find it referenced in the code? ng generate universal morningharwood-server --client-project morningharwood ...

What is the process for verifying a user's identity in Firebase authentication?

I am a beginner when it comes to using firebase. Within my nuxt js project that consists of multiple pages, the firestore rule is configured to only allow reading or writing when request.auth != null. However, when refreshing a page, the authentication wi ...