Open estatian opened 1 year ago
I have also tried this way
constructor(
private fs: Firestore,
private state: TransferState,
) {
const key = makeStateKey<unknown>('FIRESTORE')
const existing = state.get(key, undefined)
const ref = collection(this.fs, "names")
this.$items = collectionData(ref).pipe(
traceUntilFirst('firestore'),
tap(it => state.set(key, it)),
existing ? startWith(existing) : tap(),
tap(console.log),
)
}
Two months and no response... anyone know if the approach described even should work?
The description at docs/universal/getting-started.md is 5 years old and doesn't quite seem to apply anymore.
Is there an updated tutorial somewhere?
How did you create the data? On my firestore emulator suite, adding documents without creating any collection did not work.
But this first render issue sometimes occurs in other situation for me. Anyone knows?
I just added a collection in Firebase called examples
and a document which happened to be auto-ID'ed GscrMmeBDasN6llxVybm
. Could and should be anything, as far as I can understand.
Just threw in some example data like name = Example object
Stuff like that.
Can you create a simple repo using stackblitz or something. I had an issue a while back with collectionData which I later migrated away from in favor of vanilla Firestore (no angular fire)
If u make a repo I can take a look
Maybe it's related to my exact problem. I use TransferState and it just transfers the state the first render then on subsequent refreshes there is no data in the client. This is how I retrieve data:
public async getLink(userNameToCheck: string): Promise {
return new Promise<Link>(async (resolve, reject) => {
this.zone.runOutsideAngular(() => {
try {
const observable = this.afs.collection("links")
.doc(userNameToCheck + environment.secretKey)
.get()
.pipe(
map((doc) => {
if (doc.exists) {
return doc.data() as Link;
} else {
console.error("No such document!");
return null;
}
}),
take(1)
);
observable.subscribe(link => {
this.transferState.set<Link>(this.linkStateKey, link);
resolve(link);
});
} catch (error) {
reject(error);
}
});
});
}
Also had the same problem:
First compile & reload of the page the TransferState worked, but every subsequent reload failed. This only happened if I was making a call to Firestore. I think it has something to do with Firebase / AngularFire making the Angular app "Unstable".
After trying and failing to debug for 12 hours or so, I'm now using the Firebase Firestore REST API for TransferState on my initial page load. You'll have to do some parsing of this data afterwards as its in a bit of a different format.
Hope this helps someone!
import { Component } from '@angular/core';
import { SsrService } from "../../services/ssr.service";
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError, firstValueFrom } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Component({
selector: 'app-hydration',
templateUrl: './hydration.component.html',
styleUrls: ['./hydration.component.css']
})
export class HydrationComponent {
postData: any;
constructor(
private http: HttpClient,
private ssr : SsrService,
private transferState: TransferState
)
{}
async ngOnInit(): Promise<void> {
if(!this.ssr.isBrowser){
this.postData = await firstValueFrom(this.FetchData("my-post-slug"));
this.SaveState('posts', this.postData);
}
else{
if(this.HasState('posts')){
this.postData = this.GetState('posts');
console.log(this.postData);
}
else{
console.log("No data");
}
}
}
FetchData(slug: string): Observable<any> {
const query = {
structuredQuery: {
where: {
fieldFilter : {
field: { fieldPath: "slug" },
op: "EQUAL",
value: { stringValue: slug }
}
},
from: [{ collectionId: "Posts" }]
}
};
const url = 'https://firestore.googleapis.com/v1/projects/[YOUR PROJECT NAME]/databases/(default)/documents:runQuery';
return this.http.post(url, query)
.pipe(
catchError(error => {
console.error('Error:', error);
return throwError(error);
})
);
}
SaveState<T>(key: string, data: any): void {
this.transferState.set<T>(makeStateKey(key), data);
}
GetState<T>(key: string, defaultValue: any = null): T {
const state = this.transferState.get<T>(
makeStateKey(key),
defaultValue
);
return state;
}
HasState<T>(key: string) {
return this.transferState.hasKey<T>(makeStateKey(key));
}
}
SsrService
import { Injectable } from '@angular/core';
import { Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Injectable({
providedIn: 'root'
})
export class SsrService {
isBrowser : boolean;
constructor(@Inject(PLATFORM_ID) platformId: Object,) {
this.isBrowser = isPlatformBrowser(platformId);
}
}
The exact same problem as described:
The minimum example:
import { DocumentReference, Firestore, doc, docData } from "@angular/fire/firestore";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable({
providedIn: 'root',
})
export class FirebaseProxyService {
constructor(
private firestore: Firestore,
) {
}
public getDocRef(path: string, id?: string): DocumentReference {
if (id) {
return doc(this.firestore, path, id);
}
return doc(this.firestore, path);
}
public doc(collectionPath: string, id: string): Observable<any> {
console.log('this.doc', collectionPath, id);
return docData(this.getDocRef(collectionPath, id))
.pipe(
tap((docData) => {
console.log('docData', docData);
}),
);
}
}
The method that I am using is "doc", which is evaluating, returns data and console.logs (in tap operator) it only once after ssr restart (it is returns all docData which app needs, but only in first render)
Versions: angular: 15.2 @angular/fire: 7.6.1,
Version info
Angular: 15.2.0
Firebase: 9.8.0 (or whatever AngularFire pulls in...)
AngularFire: 7.5.0
Other (e.g. Ionic/Cordova, Node, browser, operating system): Node: 16.19.1 Browser: Chromium 111.0.5563.64 OS: Pop!_OS Linux
How to reproduce these conditions
Failing test unit, Stackblitz demonstrating the problem (follow steps below)
Steps to set up and reproduce
ng new ssr-test --routing=true --style=scss
cd ssr-test
ng generate environments
ng add @nguniversal/express-engine
ng add @angular/fire
(hook it up with Firestore from step 1)app.component.ts
with the following (path
inngOnInit
refers to step 1):@Component({ selector: 'app-root', template: '
' }) export class AppComponent implements OnInit {data$: Observable | null = null;
constructor(private firestore: Firestore) { }
ngOnInit() {
} }