I've created an Ionic Firebase chat app. The problem I think I'm having is I've set up a query snapshot on initialization of the message page with the following:
ngOnInit() {
this.messageService.getAllMessages()
.doc(`${this.userId[0] + '-' + this.userId[1]}`)
.collection('message')
.orderBy('createdAt', 'asc')
.onSnapshot((doc) => {
this.messages = [];
doc.forEach((snap) => {
this.messages.push({
content: snap.data().content,
createdAt: snap.data().createdAt,
userId: snap.data().userId
});
});
console.log('messages', this.messages);
});
}
The problem is if there are no messages on the initial attempt to send a message the page doesn't load the first time I attempt to go to it.
The message is a little obscure but I'm pretty sure I'm having this issue because Firebase isn't returning any data but there's no way to add a .catch in the query to catch the error and handle it so that the user can still navigate to the message page.
ERROR Error: Uncaught (in promise): Error: No value accessor for form control with unspecified name attribute
Error: No value accessor for form control with unspecified name attribute
at _throwError (vendor.js:70776)
at setUpControl (vendor.js:70646)
at NgModel._setUpStandalone (vendor.js:73693)
at NgModel._setUpControl (vendor.js:73668)
at NgModel.ngOnChanges (vendor.js:73604)
at checkAndUpdateDirectiveInline (vendor.js:63813)
at checkAndUpdateNodeInline (vendor.js:65493)
at checkAndUpdateNode (vendor.js:65432)
at debugCheckAndUpdateNode (vendor.js:66400)
at debugCheckDirectivesFn (vendor.js:66343)
at resolvePromise (polyfills.js:3193)
at resolvePromise (polyfills.js:3150)
at polyfills.js:3254
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2785)
at Object.onInvokeTask (vendor.js:57358)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (polyfills.js:2784)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (polyfills.js:2557)
at drainMicroTaskQueue (polyfills.js:2963)
defaultErrorLogger @ vendor.js:55398
handleError @ vendor.js:55450
next @ vendor.js:57952
schedulerFn @ vendor.js:52576
__tryOrUnsub @ vendor.js:133918
next @ vendor.js:133857
_next @ vendor.js:133804
next @ vendor.js:133781
next @ vendor.js:133566
emit @ vendor.js:52556
(anonymous) @ vendor.js:57389
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ polyfills.js:2753
push../node_modules/zone.js/dist/zone.js.Zone.run @ polyfills.js:2512
runOutsideAngular @ vendor.js:57315
onHandleError @ vendor.js:57389
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.handleError @ polyfills.js:2757
push../node_modules/zone.js/dist/zone.js.Zone.runGuarded @ polyfills.js:2526
_loop_1 @ polyfills.js:3056
api.microtaskDrainDone @ polyfills.js:3065
drainMicroTaskQueue @ polyfills.js:2970
Promise.then (async)
scheduleMicroTask @ polyfills.js:2946
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ polyfills.js:2775
onScheduleTask @ polyfills.js:2663
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ polyfills.js:2766
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ polyfills.js:2600
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask @ polyfills.js:2620
scheduleResolveOrReject @ polyfills.js:3241
resolvePromise @ polyfills.js:3187
(anonymous) @ polyfills.js:3103
webpackJsonpCallback @ runtime.js:26
(anonymous) @ pages-messages-messages-module.js:1
vendor.js:116244 POST https://firestore.googleapis.com/google.firestore.v1.Firestore/Write/channe
So my question is, is there a way to handle null return values in the .Snapshot query in firebase?
I've tried this:
ngOnInit() {
try {
this.messageService.getAllMessages()
.doc(`${this.users[0] + '-' + this.users[1]}`)
.collection('message')
.orderBy('createdAt', 'asc')
.onSnapshot((doc) => {
this.messages = [];
doc.forEach((snap) => {
this.messages.push({
content: snap.data().content,
createdAt: snap.data().createdAt,
userId: snap.data().userId
});
});
console.log('messages', this.messages);
});
} catch (error) {
console.log('Message page error', error);
}
}
While Doug answer is correct, you still need to catch error for onSnapshot
when permission is denied
For that, use the following syntax:
const unsubscribeMe = firestoreInstance
.collection('example')
.onSnapshot(
querySnapshot => {
if (querySnapshot.empty) {
return
}
const organizations = querySnapshot.docs.map(ref => ({
id: ref.id,
...ref.data(),
}))
},
error => {
console.log(error)
}
)
See the javascript reference here which contain additional method signature that may fit your need.
A Firestore query doesn't fail if it finds no matching documents. It simply provides a QuerySnapshot that contains no documents.
The doc
variable in your code is a QuerySnapshot type object (so, it's not a "document"). It has a method called empty() that you can use to see if it contains no documents. Alternately, you can check its docs property for zero length.
.onSnapshot(querySnapshot => {
if (!querySnapshot.empty()) {
this.messages = [];
querySnapshot.forEach((snap) => {
this.messages.push({
content: snap.data().content,
createdAt: snap.data().createdAt,
userId: snap.data().userId
});
});
}
else {
// what do you want to do if there are no documents?
}
});
Based on the suggestions I've updated my code with the following and having the same issue:
ngOnInit() {
this.messageService.getAllMessages()
.doc(`${this.users[0] + '-' + this.users[1]}`)
.collection('message')
.orderBy('createdAt', 'asc')
.onSnapshot((doc) => {
if (!doc.empty) {
this.messages = [];
doc.forEach((snap) => {
this.messages.push({
content: snap.data().content,
createdAt: snap.data().createdAt,
userId: snap.data().userId
});
});
} else {
this.messages = [];
}
console.log('messages', this.messages);
});
}
<ion-list lines="none" *ngIf="messages.length > 0">
<ion-item *ngFor="let msg of messages; index as i; trackBy: trackByCreated">
<div size="9" *ngIf="theirItinerary.userId === msg.userId" class="message other-user">
<span>{{msg.content}}</span>
<div class="time" text-right><br> {{msg.createdAt | date: 'short'}}</div>
</div>
<div offset="3" size="9" *ngIf="theirItinerary.userId !== msg.userId" class="message me" slot="end">
<span>{{msg.content}}</span>
<div class="time" text-right><br> {{msg.createdAt | date: 'short'}}</div>
</div>
</ion-item>
</ion-list>
</ion-content>
<ion-footer>
<ion-toolbar light="light">
<ion-row align-items-center no-padding>
<ion-col size="8">
<textarea autosize maxRows="3" [(ngModel)]="newMsg" class="message-input"></textarea>
</ion-col>
<ion-col size="3">
<ion-button expand="block" fill="clear" color="primary" [disabled]="newMsg === ''" class="msg-btn" (click)="sendMessage()">
<ion-icon name="ios-send" slot="icon-only"></ion-icon>
</ion-button>
</ion-col>
</ion-row>
</ion-toolbar>
</ion-footer>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With