Ionic 5 HTML variable update

Hi all,

I’m creating a pwa to scan a barcode and so far, I got this working

HTML:

  <div>
    <ion-button mode="md" size="large" expand="block" type="button" color="success" (click)="fileInput.click()">
      <ion-icon slot="start" name="barcode-outline"></ion-icon>scan barcode
    </ion-button>
    <input style="display: none" #fileInput type="file" accept="image/*" (change)="preview($event)">
  </div>

  <div class="mb-3" *ngIf="previewUrl" align="center">
    <br>
    <img [src]="previewUrl"  id="barcode"  width="320"/>
  </div>

  <div align="center" *ngIf="scanResult">
    <p style="font-weight: bolder; font-size: 25px; color: rgb(114, 114, 114);"> {{ scanResult }} </p>
  </div>

  <div align="center" *ngIf="scanError">
    <p style="font-weight: bolder; font-size: 25px; color: rgb(114, 114, 114);"> {{ scanError }} </p>
  </div>

TS:

@ViewChild("fileInput", { read: ElementRef, static: true }) fileInput: ElementRef;

  scanResult = null;
  scanError = null;

  fileData: File = null;
  previewUrl: any = null;
...
  preview(fileInput: any) {
    this.fileData = <File>fileInput.target.files[0];
    // Show preview 
    if (this.fileData != undefined) {
      if (this.fileData.type.match(/image\/*/)) {
        var reader = new FileReader();
        reader.readAsDataURL(this.fileData);
        reader.onload = () => {
          this.previewUrl = reader.result;

          const codeReader = new ZXing.BrowserBarcodeReader();
            console.log('barcode reader initialized');
            const imgnew = new Image();
            imgnew.src = this.previewUrl;

            document.body.appendChild(imgnew);
            codeReader
              .decodeFromImage(imgnew)
              .then(result => {
                this.scanError = null;
                console.log(result);
                this.scanResult = result.getText();
              })
              .catch(err => {
                this.scanResult = null;
                this.scanError = err;
                console.error(err);
              });
        },
          reader.onerror = error => console.log(error);
      }
    }
  }

The variabel “scanResult” is correct and gets immediately updated in the HTML.
But I need to resize the image to be able to decode the barcode on bigger images so I came up with this:

preview(fileInput: any) {
    this.fileData = <File>fileInput.target.files[0];
    if (this.fileData != undefined) {
     if (this.fileData.type.match(/image\/*/)) {
      const reader = new FileReader();
      reader.readAsDataURL(this.fileData);
      reader.onload = () => {
        this.previewUrl = reader.result;
        const img = new Image();
        img.src = this.previewUrl;
        img.onload = () => {
          const elem = document.createElement('canvas');
          const width = 320;
          const scaleFactor = width / img.width;
          elem.width = width;
          elem.height = img.height * scaleFactor;
          const ctx = elem.getContext('2d');
          ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
          ctx.canvas.toBlob(async (blob) => {
            const file = new File([blob], 'barcode.jpeg', {
              type: 'image/jpeg',
              lastModified: Date.now()
            });

            const codeReader = new ZXing.BrowserBarcodeReader();
            console.log('barcode reader initialized');
            const imgnew = new Image();
            imgnew.src = ctx.canvas.toDataURL(file.name);
            document.body.appendChild(imgnew);
            codeReader
              .decodeFromImage(imgnew)
              .then(result => {
                this.scanError = null;
                console.log(result);
                this.scanResult = result.getText();
              })
              .catch(err => {
                this.scanResult = null;
                this.scanError = err;
                console.error(err);
              });
          }, 'image/jpeg', 1);
        },
          reader.onerror = error => console.log(error);
      }
     }
    }
  }

The result still is correct, but my HTML does not get updated immediately anymore, only when I click the button again it is updated.

What am I doing wrong here ?

My suspicion is drawn to this line. What exactly is ZXing here (showing how it’s imported probably would go a long way towards answering this)?

You are correct, sorry for the lack of information, it’s imported like this:

import * as ZXing from '@zxing/library'

Then I’m still suspicious. I think what is happening is that you’re dancing outside of Angular’s zone. There are a bunch of ways to deal with this: the most local is to inject NgZone and wrap things in its run, but I am really not a fan of that approach because it relies overly on Angular internals that I don’t like app code messing with.

What I would suggest instead is switching to https://github.com/zxing-js/ngx-scanner, which is Angular-aware and uses the same underlying library.

Found some time to finish this project and tried another scanner module and this works as expected.

@rapropos thanks for your suggestion