Android NFC Change Detection Problem

I have a problem using an actual Ionic Angular in combination with NFC reader.

I can successfully read the NFC Tag with the example provided in:

The Problem that i have is, that after the reading of the NFC somehow the Angular ChangeDetection is broken and the changes are not displayed. When i click on a button that does nothing, the changes are displayed.

I know i can trigger the ChangeDetection manually, which would work, but then i would have to trigger the ChangeDetection everywhere in the component, which i think should not be the case.

Does anyone have an idea what i am doing wrong?

First i thought maybe it has something to do with the observable, but i tried adding a interval observable, which works just like expected.

Template:

<p><ion-button (click)="startNFC()">start NFC readermode</ion-button></p>
<p><ion-button (click)="startNFCListener()">start NFC listener</ion-button></p>
<p><ion-button (click)="startInterval()">start Interval</ion-button></p>
<p><ion-button (click)="doNothing()">do nothing</ion-button></p>
<p>
  {{nfcReading}}
  {{nfcTag}}
  <ion-spinner *ngIf="nfcReading"> </ion-spinner>
</p>

Code:

startNFC() {
  console.log("startNFC");
  this.nfcReading = true;
  let flags = this.nfc.FLAG_READER_NFC_A | this.nfc.FLAG_READER_NFC_V;

  this.readerModeSub = this.nfc.readerMode(flags).subscribe(
  tag => {
    console.log(JSON.stringify(tag));
    this.nfcTag = this.nfc.bytesToHexString(tag.id);
    this.nfcReading = false;
    this.readerModeSub.unsubscribe();

  },
  err => {
    console.log("Error reading tag", err);
    this.nfcReading = false;
  }
);

}

doNothing() {
// this really does nothing... it is just to demonstrate that this triggers the changedetection
}

i get can see that the subsciption event is triggered in console, but it is not shown in the HTML.
When clicking the do nothing Button. The tag is shown.

I created a fresh type=angular blank project and test on a Google Pixel 3a hardware.

i have uploaded a sample project to demonstrate my problem.

i found a weird and bad work-around.
I thought i share it, maybe someone comes up with a better idea or with a real solution.
i subscribe to a interval(250) before reading which also does nothing other than unsubscribe when this.nfcReading is done.
this triggers the detection.

this.readingInterval = interval(250).subscribe((_) => { 
  if (!this.nfcReading) { 
    this.readingInterval.unsubscribe(); 
  } 
});

have you solved it? I think I have your solution

No, I have not found a good solution.
Only the bad workaround to trigger the changedetection with the interval.

I have the same problem and this fixed it:

in constructor:
private cd: ChangeDetectorRef

update changes in dom:
…
this.nfcTag = this.nfc.bytesToHexString(tag.id);
this.cd.detectChanges();
…

I tried that.
This is what i meant to trigger Changedetection manually.

In my Case this worked too. But then i had issues with following changes.
The Change of the NFC Tag was detected and displayed, but other changes later where not detected and i would have had to trigger Changedetection manually after every change of the data.

The strange thing is, that the Detection worked for some Changes but not for the values that changed with the manual trigger of the change detection.

I have a list with amounts and after the NFC reading i clear all amounts.
For all Elements that had amounts added and where cleared after NFC, change Detection did not work afterwards. When i added amounts for other items, it worked, but when i added amounts for the cleared items it did not display.

let flags = this.nfc.FLAG_READER_NFC_A | this.nfc.FLAG_READER_NFC_V;
this.nfcSubscription = this.nfc.readerMode(flags)
.subscribe(
(tag) => {
…
this.updateList();
});

async updateList() {

this.xxxService.get().subscribe(
(responseData) => {

…
this.cd.detectChanges();
},
(error) => {
…
this.cd.detectChanges();
}
);
}

In html view, buttons call the same function :
<ion-button (click)=“updateList()”>
UpdateList

I think NgZone can be a solution to your problem.
Try something like:

constructor(
    private zone: NgZone,

...

 tag => {
    this.zone.run(() => {
      console.log(JSON.stringify(tag));
      this.nfcTag = this.nfc.bytesToHexString(tag.id);
      this.nfcReading = false;
      this.readerModeSub.unsubscribe();
     });
  },

I assume this happens because Angular change detection by default only listen to common browser events like mouse clicks, HTTP requests, and other types of events.

And when you receive the data from NFC reader that is not picked up by the change detection because that is not within the mentioned cases.

Some more info when to use ngZone.run()

PS: Also I would suggest outsourcing the NFC things into a service and when you receive the data just emit the changes with an Observable + Subject (I think a Subject is enough and you do not need BehaviorSubject) and only use ngZone when emitting the change like:

this.zone.run(() => {
  this.myNfcThingy.next(yourNfcData);
});
1 Like

Bearing in mind that the people who have provided suggestions so far are trying to get things done in an imperfect world, I don’t think you should have to be doing any of this - manual change detection triggering, and certainly not ever directly interacting with NgZone.

I’m not sure if @LoLStats is still active here, but getting native stuff to work seamlessly with futures (and Angular change detection as a result) was more or less the initial reason the Ionic Native project came into existence.

I would suggest filing an issue with GitHub - danielsogl/awesome-cordova-plugins: Native features for mobile apps built with Cordova/PhoneGap and open web technologies. Complete with TypeScript support., which is where I think the artist formerly known as Ionic Native has taken up residence.