angular - Hur man använder en .catch i firebase onSnapshot
Jag har skapat en Ionic Firebase-chattapp. Problemet jag tror att jag har är att jag har ställt in en frågesnapshot vid initialiseringen av meddelandesidan med följande:
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);
});
}Problemet är att om det inte finns några meddelanden vid det första försöket att skicka ett meddelande laddas inte sidan första gången jag försöker gå till den.
Meddelandet är lite otydligt men jag är ganska säker på att jag har detta problem eftersom Firebase inte returnerar någon data, men det finns inget sätt att lägga till en .catch i frågan för att fånga felet och hantera det så att användaren fortfarande kan navigera till meddelandesidan.
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/channeSå min fråga är, finns det ett sätt att hantera null-returvärden i .Snapshot-frågan i Firebase?
Jag har försökt detta:
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);
}
}Svar #1
Även om Dougs svar är korrekt, behöver du fortfarande fånga fel för onSnapshot när åtkomst nekas
För det, använd följande 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)
}
)
Se JavaScript-referensen här som innehåller ytterligare metodsignaturer som kan passa dina behov.
Svar #2
En Firestore-förfrågan misslyckas inte om den inte hittar några matchande dokument. Den ger helt enkelt en QuerySnapshot som inte innehåller några dokument.
Variabeln doc i din kod är ett QuerySnapshot-typobjekt (så det är inte ett "dokument"). Den har en metod som heter empty() som du kan använda för att se om den inte innehåller några dokument. Alternativt kan du kontrollera dess docs-egenskap för noll längd.
.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?
}
});
Baserat på förslagen har jag uppdaterat min kod med följande och har samma problem:
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>