Vue.js/TypeScript: events from component not picked up

226 Views Asked by At

I have an issue with listening to events emitted by a barcode reader, which I largely copied from GitHub.

The issue is that events, emitted by the BarcodeScanner component, aren't picked up by the encapsulating component (which is a modified HellowWorld.vue file). The browser console, with Vue DevTools, shows that the events are emitted properly. But the HelloWorld component doesn't do anything when this happens.

HelloWorld.vue:

<template>
  <div class="hello">
    <barcode-scanner @loaded="onLoaded" @decoded="onDecoded"/>
  </div>
</template>

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator';
import BarcodeScanner from "@/components/BarcodeScanner.vue";

@Component({
  components: {BarcodeScanner}
})
export default class HelloWorld extends Vue {

  onLoaded() {
    console.log("Barcode scanner component has emitLoaded");
  }

  onDecoded(code: string) {
    console.log("Scanned code", code);
  }
}
</script>

<style scoped>
</style>

BarcodeScanner.vue:

<template>
  <div class="scanner-container">
    <div v-show="isLoaded">
      <video ref="scanner"></video>
      <div class="overlay-element"></div>
    </div>
  </div>
</template>

<script lang="ts">
import {Component, Emit, Ref, Vue} from "vue-property-decorator";
import {BrowserMultiFormatReader, Exception, Result} from "@zxing/library";

@Component
export default class BarcodeScanner extends Vue {

  @Ref("scanner") private scanner!: HTMLVideoElement;
  private isLoaded = false;
  private codeReader = new BrowserMultiFormatReader();
  readonly streamAPISupported = ("enumerateDevices" in navigator?.mediaDevices);

  @Emit("loaded") emitLoaded(): void {
    this.isLoaded = true;
  }

  @Emit("decoded") emitDecoded(code: string) {
    console.log("Decoded", code);
  }

  mounted() {
    if (!this.streamAPISupported) {
      throw new Exception("Media stream API not supported");
    }
    this.start();
    this.scanner.oncanplay = () => {
      this.emitLoaded();
      console.log("Scanner has loaded (BarcodeScanner component)");
    }
  }

  start(): void {
    this.codeReader.decodeFromVideoDevice(
        null,
        this.scanner,
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        (result: Result, err: Exception | undefined) => {
          if (result) {
            console.log(result);
            this.emitDecoded(result.getText());
          }
        }
    )
  }
}
</script>

<style scoped>
video {
  max-width: 100%;
  max-height: 100%;
}

.scanner-container {
  position: relative;
}

.overlay-element {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(30, 30, 30, 0.5);
  -webkit-clip-path: polygon(
      0% 0%,
      0% 100%,
      20% 100%,
      20% 20%,
      80% 20%,
      80% 80%,
      20% 80%,
      20% 100%,
      100% 100%,
      100% 0%
  );
  clip-path: polygon(
      0% 0%,
      0% 100%,
      20% 100%,
      20% 20%,
      80% 20%,
      80% 80%,
      20% 80%,
      20% 100%,
      100% 100%,
      100% 0%
  );
}
</style>

Am I missing something obvious?

1

There are 1 best solutions below

0
On

Answering my own question: re-creating the Vue.js project and copying the above files into it solved the problem, for no obvious reason.

Coming from a Java background (and over a quarter of a century professional experience) I still find many aspects of these newfangled front-end frameworks perplexing.