How to Build Angular Barcode and QR Code Scanner Apps from Scratch
Tags: angular, webdev, qrcode, typescript
Published: 2022-06-08 00:50:10

Dynamsoft JavaScript barcode SDK is compatible with all mainstream web frameworks and web browsers. This article goes through the steps to build Angular barcode and QR code scanner apps with [Dynamsoft JavaScript barcode SDK](https://www.dynamsoft.com/barcode-reader/sdk-javascript/).

## Development Environment
- [Node.js](https://nodejs.org/en/download/)
- Angular CLI v13.3.7

    ```bash
    npm install -g @angular/cli
    
    ng --version

    Angular CLI: 13.3.7
    Node: 16.13.1
    Package Manager: npm 8.1.2
    OS: win32 x64
    
    Angular: 13.3.10
    ... animations, common, compiler, compiler-cli, core, forms
    ... platform-browser, platform-browser-dynamic, router
    
    Package                         Version
    ---------------------------------------------------------
    @angular-devkit/architect       0.1303.7
    @angular-devkit/build-angular   13.3.7
    @angular-devkit/core            13.3.7
    @angular-devkit/schematics      13.3.7
    @angular/cli                    13.3.7
    @schematics/angular             13.3.7
    ng-packagr                      13.3.1
    rxjs                            7.5.5
    typescript                      4.6.4
    ```
- [Dynamsoft JavaScript Barcode SDK v9.0.2](https://www.npmjs.com/package/dynamsoft-javascript-barcode)
    
   

To get started right away, you can visit the online project built on [StackBlitz](https://stackblitz.com/edit/angular-9f1xyn), which is a good place for editing and testing Angular code in the browser.


## Steps to Create Angular App for Scanning Barcode and QR Code 
Dynamsoft JavaScript barcode SDK provides two primary classes: `BarcodeReader` and `BarcodeScanner`. The `BarcodeReader` class supports reading barcode and QR code from static images, and the `BarcodeScanner` class supports scanning barcode and QR code from live video feeds. So in our Angular app, we will create two pages to demonstrate their functionalities respectively.

### Scaffold and configure Angular project
We create a new Angular project named `angular-barcode-qr-code-scanner` in terminal:

```bash
ng new angular-barcode-qr-code-scanner
``` 

Then install Dynamsoft JavaScript barcode SDK via npm command:

```bash
npm i dynamsoft-javascript-barcode
```

Some resource (`*.js`, `*.wasm`) files must be copied to the `assets` folder in order to make the JS barcode SDK work. Let's open the `angular.json` file to add the configuration.

```json

"build": 
{
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    ...
    "assets": [
      "src/favicon.ico",
      "src/assets",
      {
        "glob": "**/*",
        "input": "./node_modules/dynamsoft-javascript-barcode/dist",
        "output": "assets/dynamsoft-javascript-barcode"
      }
    ...
    ],
  },
  ...
},

"test": 
{
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
    ...
    "assets": [
      "src/favicon.ico",
      "src/assets",
      {
        "glob": "**/*",
        "input": "./node_modules/dynamsoft-javascript-barcode/dist",
        "output": "assets/dynamsoft-javascript-barcode"
      }
    ],
    ...
  }
}
``` 

In addition, the resource path has to be specified in TypeScript code. We create a `products.ts` file to set the license key and the resource path globally. 

```typescript
import { BarcodeReader } from 'dynamsoft-javascript-barcode';

BarcodeReader.license =
  'DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==';

BarcodeReader.engineResourcePath = 'assets/dynamsoft-javascript-barcode/';

export interface Product {
  id: string;
  name: string;
  description: string;
}

export const products = [
  {
    id: 'reader',
    name: 'Barcode and QR Code Reader',
    description: 'Scan barcode and QR code from image files',
  },
  {
    id: 'scanner',
    name: 'Barcode and QR Code Scanner',
    description: 'Scan barcode and QR code from camera stream',
  },
];

```

The `products` array will be used to create Angular components later.

### List components and add navigation
In this section, we will create the homepage that contains a top bar and a component list.

**Top bar**
1. Create a top-bar component:
    
    ```bash
    ng generate component top-bar
    ```
2. Add the top-bar component to the `app.component.html` file.
    
    ```html
    <app-top-bar></app-top-bar>
    <div class="container">
      <router-outlet></router-outlet>
    </div>
    ```
3. Add the router link to the `<H1>` title in the `top-bar.component.html`.
    
    ```html
    <a [routerLink]="['/']">
        <h1>Dynamsoft Barcode Reader and Scanner</h1>
      </a>
    ```

**Product list**
1. Create a product-list component:
    
    ```bash
    ng generate component product-list
    ```
2. Import pre-defined products to the `product-list.components.ts` file.
    
    ```typescript
    import { Component } from '@angular/core';

    import { products } from '../products';
    
    @Component({
      selector: 'app-product-list',
      templateUrl: './product-list.component.html',
      styleUrls: ['./product-list.component.css'],
    })
    export class ProductListComponent {
      products = products;
    }
    ```
3. Dynamically create the product list with `*ngFor` and `*ngIf` in the `product-list.component.html`.
    
    ```html
    
    <h2>Examples</h2>
    <div *ngFor="let product of products">
      <h3>
        <div *ngIf="product.id === 'reader'; else elseBlock">
          <a [title]="product.name + ' details'" [routerLink]="['/barcode-reader']"
            >>
            {{ product.name }}
          </a>
        </div>
        <ng-template #elseBlock
          ><a
            [title]="product.name + ' details'"
            [routerLink]="['/barcode-scanner']"
            >>
            {{ product.name }}
          </a></ng-template
        >
      </h3>
    
      <p *ngIf="product.description">Description: {{ product.description }}</p>
    </div>
    ```
4. Open the `app-routing.module.ts` file to configure the routes.
    
    ```typescript
    import { ProductListComponent } from './product-list/product-list.component';
    import { BarcodeReaderComponent } from './barcode-reader/barcode-reader.component';
    import { BarcodeScannerComponent } from './barcode-scanner/barcode-scanner.component';
    
    const routes: Routes = [
      { path: '', component: ProductListComponent },
          { path: 'barcode-reader', component: BarcodeReaderComponent },
          { path: 'barcode-scanner', component: BarcodeScannerComponent },
    ];
    ```

    ![Angular top bar component](https://www.dynamsoft.com/codepool/img/2022/06/angular-top-bar-component.png)

    The `ProductListComponent` is configured as the homepage. We will create the `BarcodeReaderComponent` and `BarcodeScannerComponent` in the following paragraphs.
    
    
### Create the Barcode Reader component

1. Create the `barcode-reader` component.    
    
    ```bash
    ng generate component barcode-reader
    ```
2. Add `HTMLInputElement`, `HTMLCanvasElement`, and `HTMLImageElement` to the `barcode-reader.component.html` file.
    
    ```html
    <span id="loading-status" style="font-size:x-large" [hidden]="isLoaded"
      >Loading Library...</span
    >
    <br />
    
    <input type="file" id="file" accept="image/*" (change)="onChange($event)" />
    <div>
      <a id="result"></a>
    </div>
    
    <div id="imageview">
      <img id="image" />
      <canvas id="overlay"></canvas>
    </div>

    ```
   
    The `change` event is bound to the `onChange` method, which is defined in the `barcode-reader.component.ts` file.
3. Initialize the `BarcodeReader` object in the `barcode-reader.component.ts` file.
    
    ```typescript
    import { Component, OnInit } from '@angular/core';
    import { BarcodeReader } from 'dynamsoft-javascript-barcode';
    
    @Component({
      selector: 'app-barcode-reader',
      templateUrl: './barcode-reader.component.html',
      styleUrls: ['./barcode-reader.component.css'],
    })
    export class BarcodeReaderComponent implements OnInit {
      isLoaded = false;
      overlay: HTMLCanvasElement | undefined;
      context: CanvasRenderingContext2D | undefined;
      reader: BarcodeReader | undefined;
    
      constructor() {}
    
      ngOnInit(): void {
        this.initOverlay();
        (async () => {
          this.reader = await BarcodeReader.createInstance();
          this.isLoaded = true;
        })();
      }
    }
    ``` 
4. In the `onChange` method, we firstly load an image file with `FileReader` and then draw barcode and QR code results on the canvas which is above the image. The size of the canvas is determined by the image size.

    ```typescript
    onChange(event: Event) {
        const element = event.currentTarget as HTMLInputElement;
        let fileList: FileList | null = element.files;
        if (fileList) {
          let file = fileList.item(0) as any;
          if (file) {
            let fr = new FileReader();
            fr.onload = (event: any) => {
              let image = document.getElementById('image') as HTMLImageElement;
              if (image) {
                image.src = event.target.result;
                const img = new Image();
    
                img.onload = (event: any) => {
                  this.updateOverlay(img.width, img.height);
                  if (this.reader) {
                    this.reader.decode(file).then((results: any) => {
                      console.log(results);
                      let txts: any = [];
                      let elem = document.getElementById('result');
                      try {
                        let localization;
                        if (results.length > 0) {
                          for (var i = 0; i < results.length; ++i) {
                            txts.push(results[i].barcodeText);
                            localization = results[i].localizationResult;
                            this.drawOverlay(
                              localization,
                              results[i].barcodeText
                            );
                          }
    
                          if (elem) {
                            elem.innerHTML = txts.join(', ');
                          }
                        } else {
                          if (elem) {
                            elem.innerHTML = txts.join(', ');
                          }
                        }
                      } catch (e) {
                        alert(e);
                      }
                    });
                  }
                };
                img.src = event.target.result;
              }
            };
            fr.readAsDataURL(file);
          }
        }
      }
    ``` 
    
    ![Angular barcode and QR code reader](https://www.dynamsoft.com/codepool/img/2022/06/angular-barcode-qrcode-reader.png)

### Create the Barcode Scanner component
1. Create the `barcode-scanner` component:    
    
    ```bash
    ng generate component barcode-scanner
    ```
2. Add `HTMLSelectElement` and `HTMLCanvasElement` to the `barcode-scanner.component.html` file. Dynamsoft barcode scanner has encapsulated the video element. It simplifies the process of writing JavaScript code for camera control.
    
    ```html
    <span id="loading-status" style="font-size:x-large" [hidden]="isLoaded"
      >Loading Library...</span
    >
    <br />
    
    <div class="select">
      <label for="videoSource">Video source: </label>
      <select id="videoSource" (change)="openCamera()"></select>
    </div>
    
    <div id="videoview">
      <div class="dce-video-container" id="videoContainer"></div>
      <canvas id="overlay"></canvas>
    </div>

    ```

3. Initialize the `BarcodeScanner` object in the `barcode-scanner.component.ts` file.
    
    ```typescript
    import { Component, OnInit } from '@angular/core';
    import { BarcodeScanner } from 'dynamsoft-javascript-barcode';
    
    @Component({
      selector: 'app-barcode-scanner',
      templateUrl: './barcode-scanner.component.html',
      styleUrls: ['./barcode-scanner.component.css'],
    })
    export class BarcodeScannerComponent implements OnInit {
      isLoaded = false;
      overlay: HTMLCanvasElement | undefined;
      context: CanvasRenderingContext2D | undefined;
      scanner: BarcodeScanner | undefined;
      cameraInfo: any = {};
      videoSelect: HTMLSelectElement | undefined;
    
      constructor() { }
    
      ngOnInit(): void {
        this.videoSelect = document.querySelector('select#videoSource') as HTMLSelectElement;
        this.initOverlay();
        (async () => {
          await this.initBarcodeScanner();
        })();
      }

        async initBarcodeScanner(): Promise<void> {
            this.scanner = await BarcodeScanner.createInstance();
            this.isLoaded = true;
            await this.scanner.updateRuntimeSettings("speed");
            let uiElement = document.getElementById('videoContainer');
            if (uiElement) {
              await this.scanner.setUIElement(uiElement);
              let cameras = await this.scanner.getAllCameras();
              this.listCameras(cameras);
              await this.openCamera();
              this.scanner.onFrameRead = results => {
                this.clearOverlay();
        
                let txts = [];
                let resultElement = document.getElementById('result');
                try {
                  let localization;
                  if (results.length > 0) {
                    for (var i = 0; i < results.length; ++i) {
                      txts.push(results[i].barcodeText);
                      localization = results[i].localizationResult;
                      this.drawOverlay(localization, results[i].barcodeText);
                    }
                    if (resultElement) {
                      resultElement.innerHTML = txts.join(', ');
                    }
                  }
                  else {
                    if (resultElement) {
                      resultElement.innerHTML = "No barcode found";
                    }
                  }
        
                } catch (e) {
                  alert(e);
                }
              };
              this.scanner.onPlayed = () => {
                this.updateResolution();
              }
              await this.scanner.show();
            }
          }
    ```
    
    We can get the barcode and QR code results by registering the `onFrameRead` event. The `onFrameRead` event is triggered when the barcode and QR code results are ready.
    
    ![Angular barcode and QR code scanner](https://www.dynamsoft.com/codepool/img/2022/06/angular-barcode-qrcode-scanner.png)


### Run the Angular project with SSL
Web camera access requires `HTTPS` protocol. Therefore, we use the `ng serve --ssl` command to run the Angular project. Without `ssl`, the `NotAllowedError: Permission denied` error is thrown:

![Angular camera permission error](https://www.dynamsoft.com/codepool/img/2022/06/angular-camera-permission-error.png)

## GitHub Page Deployment
After pushing the source code to GitHub, we can deploy the Angular project to GitHub pages with GitHub actions.

Here are the steps:
1. Create a custom workflow file including [Angular Deploy gh-pages Actions](https://github.com/marketplace/actions/angular-deploy-gh-pages-actions):

    ```yml
    name: Build and Deploy
    on:
      push:
        branches:
          - main
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v2
        - name: All things angular
          uses: AhsanAyaz/angular-deploy-gh-pages-actions@v1.3.2
          with:
            github_access_token: ${{ secrets.GITHUB_TOKEN }} 
            build_configuration: production 
            base_href: /angular-barcode-qr-code-scanner/   
            deploy_branch: gh-pages 
            angular_dist_build_folder: dist/angular-barcode-qr-code-scanner
    
    ```
    You need to replace `angular-barcode-qr-code-scanner` with your project name.
2. Go to `Your GitHub repository > Settings > Pages` to launch the GtiHub page with the `gh-pages` branch. The sample project page is available at [https://yushulx.me/angular-barcode-qr-code-scanner/](https://yushulx.me/angular-barcode-qr-code-scanner/).


## Source Code
[https://github.com/yushulx/angular-barcode-qr-code-scanner](https://github.com/yushulx/angular-barcode-qr-code-scanner)