离子:ngFor 在数组更改后不会自动更新

ionic: ngFor not updating automatically after changes in array

这里是 Ionic 新手。我正在使用 Ionic 3 和 BLE 插件。这个插件的工作方式是你开始扫描蓝牙设备,你会收到每个新扫描结果的通知,然后当你完成扫描时取消扫描。每次收到新的扫描结果时,我只是想将一个元素附加到 ion-list 中。

此 Cordova 插件使用离子包装到 Observables 和 Promises 中的回调。方法 startScan returns 和 Observable<any>,"any" 是一个包含有关检测到的 BLE 设备的信息的对象。

我首先尝试将这个 observable 直接插入 ngFor:

<ion-list>
    <button ion-item *ngFor="let result of results | async">
        {{ result | json}}
    </button>  
</ion-list>

开始扫描的调用返回了 observable:

this.results = this.ble.startScan([]);
this.results.subscribe(...);

不过我听说 ngFor 只适用于数组,所以它需要一个 Observable<Array> 而不是单个对象的可观察对象。所以我放弃了 Observable 并改用数组。异步管道不再工作,所以我不得不修改列表:

<ion-list>
    <button ion-item *ngFor="let result of results">
        {{ result | json}}
    </button>  
</ion-list>

然后将 results 的类型更改为 Array<any>。扫描码现在是这样的:

this.ble.startScan([])
.subscribe(device => {
    this.results.push(device); //Does not work    
});

但是直到屏幕中的其他一些组件发生变化时才会显示列表。显然 Angular 不检测数组元素内部的变化,它只检测对象内部引用和属性的变化。所以我尝试了这个无能的黑客:

this.ble.startScan([])
.subscribe(device => {
    this.results = this.results.concat([device]); //Does not work    
});

但即使这样也没有用。然后经过几个小时的阅读,我知道了这个叫做 ChangeDetector 的东西,据称它应该可以解决问题。我尝试了OnPush检测策略,默认也没有用:

this.ble.startScan([])
.subscribe(device => {
    this.results = this.results.concat([device]);
    this.changeDetector.markForCheck() //Does not work    
});

当然它不起作用,因为它只是标记为检查,但不在那个时刻执行检查。

TL;DR ELI5 你究竟需要在 Ionic(或 Angular)中做什么才能将元素添加到列表中?

您根本不必将数据推送到列表中。

   Consider you are returning data

    shoppingItems: FirebaseListObservable<any[]>;

    this.shoppingItems = af.list('/Items', {
        query: {
            limitToLast: 1000

        }
    });


   If you are not using firebase then just return the data from service directly as below.

 this.shoppingItems = http('your service url'); 

HTML

<ul *ngFor="let item of shoppingItems | async" >

<li>{{item.name}} </li>

</ul>

尝试 detectChanges() 而不是 markForCheck()

也许您想看看 this aproach

作者使用 ngZones run() 将找到的设备添加到列表中,其中包括 changeDetection。非常有趣恕我直言。这是一个nice article about ngZone

这就是最终起作用的方法:

this.ble.startScan([])
.subscribe(device => {
    this.results.push(device);
    this.changeDetector.detectChanges();
});

我发现的另一个解决方案是在页面上使用对 Angular 应用程序 运行 的引用,请参阅以下 link,并显式调用它的 tick() 方法过程变化检测及其副作用。我在 Ionic 中所做的如下:

import { ApplicationRef } from '@angular/core';
export class HomePage {
  constructor ( private app: ApplicationRef ) {}

  .. my code ...

  this.app.tick();  //start change detection to cause a screen update
}