Search code examples
node.jsfirebasegoogle-cloud-firestoreangularfire

AngularFire Quickstart Not Returning Data From Firestore


  1. Followed steps in AngularFire Quickstart
  2. Add authentication as described in 5. Getting started with Firebase Authentication

When I set my Firestore rules to limit read and write access to authenticated users, I either get an error or I get nothing. For more details, see Issue #2838 filed in the GitHub repository.

My environment is:

  • Angular CLI: 12.0.1
  • Node: 14.17.0
  • Package Manager: npm 7.13.0
  • AngularFire: 6.1.5
  • Firebase: 8.6.1
  • Firebase Tools: 9.11.0
  • OS: Ubuntu 20.04.2 LTS (linux x64)

My Firestore rules are:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if request.auth != null;
      allow write: if request.auth != null;
    }
  }
}

app.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { AngularFireAuth } from '@angular/fire/auth';
import firebase from 'firebase/app';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'Angular Fire Quickstart';
  userDisplayName: string | null = null;
  leagues$: Observable<any[]> = new Observable<any[]>();

  constructor(
    private firestore: AngularFirestore,
    public auth: AngularFireAuth) {}

  ngOnInit() {
    // Recommended in Firebase documentation
    this.auth.onAuthStateChanged((user) => {
      if (user) {
        this.userDisplayName = user.displayName;
        this.leagues$ = this.firestore.collection('Leagues').valueChanges();
      } else {
        this.userDisplayName = null;
        this.leagues$ = new Observable<any[]>();
      }
    });
}
  login() {
    this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
  }
  logout() {
    this.auth.signOut();
  }
}

app.component.html

<div *ngIf="userDisplayName != null; else showLogin">
  <p>Hello {{userDisplayName}}.
    <button (click)="logout()">Logout</button>
  </p>
  <ul>
    <li *ngFor="let league of leagues$ | async">
      {{ league.name }} - {{ league.location }}
    </li>
  </ul>
</div>
<ng-template #showLogin>
  <p>Please login.</p>
  <button (click)="login()">Login</button>
</ng-template>

Solution

  • Resolved by subscribing to the Observable and changing my template to watch an array managed by the subscription. I now get data every time.

    Here's the code with the changes:

    app.component.ts

    ...
    export class AppComponent implements OnInit {
      title = 'Angular Fire Quickstart';
      theUser: firebase.User | null = null;
      leagueArray: Array<any> = [];   // Eliminated the Observable in favor of an Array
    
      constructor(
        private firestore: AngularFirestore,
        public auth: AngularFireAuth) {}
    
      ngOnInit() {
        this.auth.onAuthStateChanged((user) => {
          if (user) {
            this.theUser = user;
    
            // Added the subscription and populated the array from there.
            this.firestore.collection('Leagues').valueChanges().subscribe((data) => {
              data.forEach((item) => {
                this.leagueArray.push(item);
              });
            });
          } else {
            this.theUser = null;
            this.leagueArray = [];
          }
        });
      }
    ...
    

    app.component.html

    <div *ngIf="theUser != null">
      <p>Hello {{theUser.displayName}}.
        <button (click)="logout()">Logout</button>
      </p>
      <ul>
        <!-- Watch the array instead of an Observable -->
        <li *ngFor="let league of leagueArray">
          {{ league.name }} - {{ league.location }}
        </li>
      </ul>
    </div>