离子新手在这里。我正在使用Ionic 3和BLE插件。此插件的工作方式是您开始扫描蓝牙设备,每次新的扫描结果都会通知您,然后在您完成后取消扫描。我只是想在每次收到新的扫描结果时将元素附加到ion-list
中。
这个Cordova插件使用回调,其中离子包含在Observables和Promises中。方法startScan
返回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不会检测Array元素内部的更改,它只检测对象内部引用和属性的更改。所以我尝试了这个不合适的黑客攻击:
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)中你需要做什么才能在列表中添加一个元素?
试试detectChanges()
而不是markForCheck()
。
也许你想看看this aproach。
作者使用ngZones run()
将找到的设备添加到列表中,其中包括changeDetection。非常有趣的imho。这是一个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
}
您根本不必将数据推送到列表中。
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>