在 NativeScript ListView 中异步显示来自 Firebase 的消息

Display messages from Firebase asynchronously in NativeScript ListView

我正在尝试在 ListView 中异步显示简单消息。这些消息是通过 the de-facto standard NativeScript Plugin 从 Firebase 获取的。我认为我的插件交互没有任何问题,因为我能够初始化、登录甚至接收消息。我只是无法让它们显示在 ListView 中。

我设法通过创建 EventEmitter 将事件回调从 Firebase 消息推送映射到可观察对象。我不确定这是否是正确的方法,但它显示了最好的结果,因为我可以看到通过我的 observable 推送的消息。

这是我正在做的事情:

import { Component, OnInit, EventEmitter } from "@angular/core";
import { Observable } from "rxjs/Observable";
import * as firebase from "nativescript-plugin-firebase";
import { Message } from "./shared/message.model";
@Component({
    selector: "my-app",
    templateUrl: "app.component.html"
})
export class AppComponent implements OnInit {
    public messages: Observable<Array<Message>>;

    ngOnInit() {
        this.init()
            .then(() => this.login())
            .then(() => this.getMessages());
    }

   // ... implementations for init() and login(), also post()-method

    getMessages = () => {
        let msgs = [];
        // initialize with asynchronous behaviour
        let emitter = new EventEmitter(true);
        this.messages = emitter;
        // shows messages getting added to array
        this.messages.subscribe((a) => console.log(JSON.stringify(a)));

        firebase.addChildEventListener((result) => {
            msgs.push(new Message(result.value.text, result.value.timestamp));
            emitter.next(msgs);
        }, "/messages");
    }
}

这是视图:

<GridLayout rows="auto, *">
    <GridLayout row="0" columns="*, auto">
        <TextField #textInput hint="Type message..." col="0"></TextField>
        <Button text="Post" (tap)="post(textInput)" col="1"></Button>
    </GridLayout>

    <ListView row="1" [items]="messages | async">
        <template let-item="msg">
            <StackLayout>
                <Label text="a message"></Label> <!-- displays after button click -->
                <Label [text]="msg.text"></Label> <!-- results in exception when there even are items -->
            </StackLayout>
        </template>
    </ListView>
</GridLayout>

我可以看到订阅工作正常,因为每个数组变体都被增量记录,但我的视图没有显示它们。我为 ChangeDetectionStragety 尝试了不同的模式,这似乎没有什么不同。

我发现当我的视图中有一个调用组件函数的按钮时,我可以在单击它后看到元素。但是,如果在我看来存在对 let 变量的绑定,例如 [text]="msg.text",则应用程序会崩溃并出现以下错误:

An uncaught Exception occurred on "main" thread.
com.tns.NativeScriptException: 
Calling js method getView failed
[object Object]
File: "/data/data/org.nativescript.firebasetest/files/app/tns_modules/@angular/core/bundles/core.umd.js, line: 9464, column: 20
StackTrace: 
Frame: function:'ListViewComponent.detectChangesOnChild', file:'/data/data/org.nativescript.firebasetest/files/app/tns_modules/nativescript-angular/directives/list-view-comp.js', line: 134, column: 29
Frame: function:'ListViewComponent.onItemLoading', file:'/data/data/org.nativescript.firebasetest/files/app/tns_modules/nativescript-angular/directives/list-view-comp.js', line: 114, column: 14
Frame: function:'Observable.notify', file:'/data/data/org.nativescript.firebasetest/files/app/tns_modules/data/observable/observable.js', line: 146, column: 32
Frame: function:'ListViewAdapter.getView', file:'/data/data/org.nativescript.firebasetest/files/app/tns_modules/ui/list-view/list-view.js', line: 199, column: 28  
// ... native stacktrace

我做错了什么?为什么在 Angular 中很容易在 NativeScript 中实现异步列表却如此困难?这里的文档有点薄弱..

您的 firebase 连接器 运行 在 angular 区域之外,也许将您的下一个呼叫包装在 ngZone.run 中有助于:

import { Component, OnInit, EventEmitter, NgZone } from "@angular/core";
import { Observable } from "rxjs/Observable";
import * as firebase from "nativescript-plugin-firebase";
import { Message } from "./shared/message.model";
@Component({
    selector: "my-app",
    templateUrl: "app.component.html"
})
export class AppComponent implements OnInit {
    public messages: Observable<Array<Message>>;

    constructor(private ngZone: NgZone) {}

    ngOnInit() {
        this.init()
            .then(() => this.login())
            .then(() => this.getMessages());
    }

   // ... implementations for init() and login(), also post()-method

    getMessages = () => {
        let msgs = [];
        // initialize with asynchronous behaviour
        let emitter = new EventEmitter(true);
        this.messages = emitter;
        // shows messages getting added to array
        this.messages.subscribe((a) => console.log(JSON.stringify(a)));

        firebase.addChildEventListener((result) => {
            this.ngZone.run(() => {
                msgs.push(new Message(result.value.text, result.value.timestamp));
                emitter.next(msgs);
            });
        }, "/messages");
    }
}

这也可能有帮助:https://github.com/NathanWalker/angular-seed-advanced/wiki/How-to-integrate-Firebase-across-all-platforms-(web-nativescript-desktop)

除了与 ngZone 相关的问题外,项目模板中还存在语法错误。将此 <template let-item="msg"> 更改为

<template let-msg="item">

详情见