Felipe Micali
Felipe Micali

Reputation: 847

Instascan QR Code scanner 'scan' listener won't update Angular 5 component view

I've implemented Instascan (https://github.com/schmich/instascan) to allow users to read QR Codes from within my Angular 5 app.

I have to trigger some actions after a successful scan and update my component's view accordingly.

I'm using the following code inside my component to detect my cameras and start scanning:

cameras: Array<any> = []
selectedCamera: any
scanner: any
content: string

ngOnInit () {
    let vm = this
    Instascan.Camera.getCameras().then(function (cameras) {
      if (cameras.length > 0) {
        vm.cameras.push(...cameras)
      } else {
        console.error('No cameras found.')
      }
    }).catch(function (e) {
      console.error(e)
    })
  }

startScan () {
    let vm = this
    this.scanner = new Instascan.Scanner({
      video: this.videoContainer.nativeElement,
      backgroundScan: false,
      mirror: false
    })
    this.scanner.addListener('scan', function (content) {
      vm.content = content
    })
    this.selectedCamera = this.cameras[0]
    this.scanner.start(this.selectedCamera)
  }

And in my template I have an element that exists only if 'content' exists, and on click emit the scanned content to the parent component through an EventEmitter:

<div *ngIf="content" class="btn" (click)="emitContent()">
          PROCEED
        </div>

The problem is that in 'scan' event callback the changes in 'content' seems to don't be applied to my view, and my 'PROCEED' button don't become visible. An even stranger behavior happens: after I click anywhere in the screen, those changes are applied to my view.

I've also tried using ChangeDetectorRef.detectChanges() method inside the callback and binding 'this' into the callback, but both are not working.

How can I overcome this issue?

Thanks!

Upvotes: 0

Views: 1666

Answers (1)

Felipe Micali
Felipe Micali

Reputation: 847

I've managed to solve this problem by using NgZone like this:

import { NgZone } from '@angular/core'

constructor (
    private ngZone: NgZone
  ) {}

startScan () {
    this.scanner = new Instascan.Scanner({
      video: this.videoContainer.nativeElement,
      backgroundScan: false,
      mirror: false
    })
    this.scanner.addListener('scan', function (content) {
      this.ngZone.run(() => {
        this.content = content
      })
    }.bind(this))
    this.selectedCamera = this.cameras[0]
    this.scanner.start(this.selectedCamera)
  }

I don't know if this is the best solution, and actually I don't know how NgZone usage affects the application performance/state at all.

If someone know a better solution, it will be welcome!

Thanks!

Upvotes: 1

Related Questions