如何在 Firebase onSnapshot 中使用 .catch
Posted
技术标签:
【中文标题】如何在 Firebase onSnapshot 中使用 .catch【英文标题】:How to use a .catch in firebase onSnapshot 【发布时间】:2020-07-26 15:53:38 【问题描述】:我创建了一个 Ionic Firebase 聊天应用。我认为我遇到的问题是我在消息页面初始化时设置了一个查询快照,其中包含以下内容:
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);
);
问题是,如果第一次尝试发送消息时没有消息,则页面在我第一次尝试访问时不会加载。
消息有点晦涩,但我很确定我遇到了这个问题,因为 Firebase 没有返回任何数据,但无法在查询中添加 .catch 来捕获错误并进行处理,以便用户仍然可以导航到消息页面。
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
所以我的问题是,有没有办法在 firebase 的 .Snapshot 查询中处理 null 返回值?
我试过了:
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);
【问题讨论】:
回到你原来的问题:“你如何处理快照上的错误”——有没有办法?我不相信您可以将捕获链接到它,我很困惑,因为它没有告诉您哪个“onSnapshot”也有错误,并且如果没有堆栈跟踪,它会使事情难以调试。 【参考方案1】:虽然Doug answer 是正确的,但您仍然需要在权限被拒绝时捕获onSnapshot
的错误
为此,请使用以下语法:
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)
)
请参阅javascript reference here,其中包含可能满足您需要的其他方法签名。
【讨论】:
【参考方案2】:如果没有找到匹配的文档,Firestore 查询不会失败。它只是提供一个不包含任何文档的 QuerySnapshot。
代码中的doc
变量是QuerySnapshot 类型的对象(因此,它不是“文档”)。它有一个名为empty() 的方法,您可以使用它来查看它是否不包含任何文档。或者,您可以检查其 docs 属性是否为零长度。
.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?
);
根据建议,我用以下内容更新了我的代码并遇到了同样的问题:
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>
【讨论】:
谢谢道格。您的 sn-p 帮助我确定查询没有导致问题,而是其他问题。我已经根据您的建议添加了我正在使用的 html 和更新的代码。您能看到其他可能导致此问题的原因吗? 尝试对错误消息进行网络搜索。 “具有未指定名称属性的表单控件没有值访问器” 我想通了...... html 的标签依赖于来自 firebase 的数据。我为属性添加了一个 ngIf 并解决了这个问题。以上是关于如何在 Firebase onSnapshot 中使用 .catch的主要内容,如果未能解决你的问题,请参考以下文章
firebase onSnapshot 如何刷新数组以避免重复键?
如何使用 Firebase“onSnapshot”侦听器值更新 Vue“ref”?
Firebase firestore onSnapshot 在 useEffect 中无法正常工作
cleanup() 在使用 Firebase Firestore onSnapshot 的 useEffect() 中不起作用