Reputation: 28199
Ionic newbie here. I'm using Ionic 3 and the BLE plugin. The way this plugin works is you start scanning for Bluetooth devices, you are notified with each new scan result, and then when you are done you cancel the scan. I'm just trying to append an element into an ion-list
each time a new scan result is received.
This Cordova plugin uses callbacks which ionic wraps into Observables and Promises. The method startScan
returns an Observable<any>
, and that "any" is an object containing info about a detected BLE device.
I first tried to plug this observable directly into the ngFor:
<ion-list>
<button ion-item *ngFor="let result of results | async">
{{ result | json}}
</button>
</ion-list>
The call to start scan returned the observable:
this.results = this.ble.startScan([]);
this.results.subscribe(...);
However I heard that ngFor
only works with arrays, so it would need an Observable<Array>
instead of an observable of a single object. So I ditched the Observable and used an array instead. The async pipe no longer worked so I had to modify the list:
<ion-list>
<button ion-item *ngFor="let result of results">
{{ result | json}}
</button>
</ion-list>
And then changed the type of results
to Array<any>
. The scanning code now looks like this:
this.ble.startScan([])
.subscribe(device => {
this.results.push(device); //Does not work
});
But the list is not displayed until some other components in the screen change. Apparently Angular does not detect the change inside the Array elements, it only detects changes to references and properties inside objects. So I've tried this inneficient hack:
this.ble.startScan([])
.subscribe(device => {
this.results = this.results.concat([device]); //Does not work
});
But even that didn't work. Then after some hours of reading I knew about this thing called ChangeDetector
which allegedly should do the trick. I tried the OnPush detection strategy and also the default to no avail:
this.ble.startScan([])
.subscribe(device => {
this.results = this.results.concat([device]);
this.changeDetector.markForCheck() //Does not work
});
And of course it doesn't work because it just marks for check, but does not perform the checking at that very moment.
TL;DR ELI5 what on Earth do you need to do in Ionic (or Angular) to add an element to a list?
Upvotes: 3
Views: 9479
Reputation: 59
One other solution I found is to use a reference to an Angular application running on the page, see the following link, and call it's tick() method to explicitly process change detection and its side-effects. What I did in Ionic is the following:
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
}
Upvotes: 3
Reputation: 28199
This is what finally worked:
this.ble.startScan([])
.subscribe(device => {
this.results.push(device);
this.changeDetector.detectChanges();
});
Upvotes: 4
Reputation: 701
Try detectChanges()
instead of markForCheck()
.
And maybe you want to take a look at this aproach.
The author uses ngZones run()
to add found devices to a list, which includes changeDetection. Pretty interesting imho. Here is a nice article about ngZone
Upvotes: 9
Reputation: 949
You dont have to push the data into a list at all.
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>
Upvotes: -3