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