session.startDate is a firebase timestamp, and I'm converting it with the toDate() function and a date pipe in my HTML. This worked great, until I started adding documents through a service.
{{session.startDate.toDate() | date}}
After adding in a new document, while everything would render correctly on the screen I am getting a ton of angry console messages:
ERROR TypeError: Cannot read properties of null (reading 'toDate')
So, I'm guessing when the service is adding the document there is enough time before startDate is populated that the .toDate() function fails until the data is there.
I'm sure what I'm trying to do is pretty simple, I'm just not sure at what point in my project I should be waiting for the data, and the best way to do that.
For this, I'm using 2 components: the display component, and the service component.
Display Component When the display first loads, I'm grabbing the current route ID, and setting sessionSource to the subscribed sessionService.
ngOnInit(): void {
this.subscribe = this.activatedRoute.paramMap.subscribe(params => {
this.pid = params.get('pid');
})
this.sessionService.getSessionList(this.pid).subscribe((value) => {
this.sessionSource = value
});
}
Then in the HTML, I'm running *ngFor to display a list of the different documents.
<mat-expansion-panel *ngFor=" let session of sessionSource"
<mat-expansion-panel-header>
<mat-panel-title>
{{session.startDate.toDate() | date}}
</mat-panel-title>
<mat-panel-description>
{{session.sessionDescription}}<div *ngIf="session.active"> - Active Session</div>
</mat-panel-description>
</mat-expansion-panel-header>
</mat-expansion-panel>
Session Service When I use my service to add a new session, this is what is running (note, there is some in progress 'return' functionality to get the newly created document ID back for routing that I haven't got working yet).
This succeeds in adding a new entry, and it displays on the page as expected, but with the aforementioned errors.
startNewSession(pid: string){
return this.afs.collection("/Management/" pid "/Sessions").add({
active: true,
startDate: firebase.firestore.FieldValue.serverTimestamp(),
sessionDescription: 'Testing Adding'
}).then(docRef => {
return docRef.id
}).catch(error => console.error("Error adding document: ", error))
}
CodePudding user response:
Alright, the solution is surprisingly simple but it took me a while to even figure out what the problem was. In the end, I went back to an article I had read when I was first working with firebase timestamps on medium. There is a lot of detail, but the upshot is that until the timestamp is finalized it's going to default returning a null value, which is what was causing my null error.
The solution is to use 'SnapshotOptions' to modify each documentsnapshot with an estimate of what the timestamp would be. I don't need nanosecond precision, so this is perfect for my usage.
In the end, my change wasn't even in something I had originally posted, it's inside of the service and is what returns the subscription for sessionSource in ngOnIt.
Below is the entire section, but all I did was add { serverTimestamps: 'estimate' } as an option to the payload.doc.data() function. This set's an estimated timestamp, and I no longer have the null errors.
getSessionList(pid: string){
return this.afs.collection(this.CollectionPath pid '/Sessions', ref => ref.orderBy('startDate', 'desc')).snapshotChanges().pipe(
map(actions => {
return actions.map(a => {
const data = a.payload.doc.data({ serverTimestamps: 'estimate' }) as sessions_list;
const id = a.payload.doc.id;
return { id, ...data };
})
}
))
}
There is a similar question I found while looking for a solution.