How to Build an Angular Barcode & QR Code Detection App: A Step-by-Step Guide
From 9.x to 10.x, the Dynamsoft JavaScript barcode SDK has undergone a complete redesign. The new version not only provides essential barcode scanning functionalities but also integrates seamlessly with other Dynamsoft products, such as Dynamsoft Label Recognizer, Dynamsoft Camera Enhancer, Dynamsoft Document Normalizer, and Dynamsoft Code Parser. All Dynamsoft products are now unified under the Dynamsoft Capture Vision architecture. In this article, we will demonstrate how to build Angular barcode and QR code scanner apps from scratch using the Dynamsoft JavaScript Barcode SDK v10.x.
This article is Part 2 in a 5-Part Series.
- Part 1 - How to Digitize Paper Documents in Angular Web Applications
- Part 2 - How to Build an Angular Barcode & QR Code Detection App: A Step-by-Step Guide
- Part 3 - How to Detect and Rectify Documents in Angular Web Applications
- Part 4 - Steps to Develop an Angular Passport MRZ Reader & Scanner
- Part 5 - Building an Angular Document Viewer for Image Loading, Annotation, and PDF Export
Angular 1D/2D Barcode Scanner
Online Demo
Prerequisites
- Node.js
-
Install Angular CLI for Angular project development:
npm install -g @angular/cli ng --version
- Obtain a 30-Day Trial License for Dynamsoft JavaScript Barcode SDK v10.x.
Step1: Scaffold an Angular Barcode and QR Code Scanner Project
Create a New Angular Project
First, create a new Angular project named angular-barcode-qr-code-scanner
using the Angular CLI:
ng new angular-barcode-qr-code-scanner
Install Required Packages
Next, install the necessary Dynamsoft packages via npm:
npm i dynamsoft-barcode-reader-bundle dynamsoft-image-processing dynamsoft-capture-vision-std
-
dynamsoft-barcode-reader-bundle: The package organizes different versions of Dynamsoft JavaScript products to ensure compatibility.
- dynamsoft-image-processing: The package provides image processing functionalities.
- dynamsoft-capture-vision-std: The package is a collection of classes and functions that facilitate the execution of other modules within the Dynamsoft Capture Vision architecture.
Configure Angular to Use Third-Party JavaScript Libraries
To make the third-party JavaScript libraries work in Angular, follow these steps:
-
Open the
angular.json
file and add the following configuration under thebuild
section:"build": { "builder": "@angular-devkit/build-angular:browser", "options": { ... "assets": [ "src/favicon.ico", "src/assets", { "glob": "**/*", "input": "./node_modules/dynamsoft-capture-vision-std/dist", "output": "assets/dynamsoft-capture-vision-std" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-image-processing/dist", "output": "assets/dynamsoft-image-processing" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-core/dist", "output": "assets/dynamsoft-core" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-license/dist", "output": "assets/dynamsoft-license" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-capture-vision-router/dist", "output": "assets/dynamsoft-capture-vision-router" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-barcode-reader/dist", "output": "assets/dynamsoft-barcode-reader" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-camera-enhancer/dist", "output": "assets/dynamsoft-camera-enhancer" } ... ], }, ... },
-
In your TypeScript code, set the resource output paths as follows:
import { CoreModule, LicenseManager } from 'dynamsoft-barcode-reader-bundle'; CoreModule.engineResourcePaths = { std: 'assets/dynamsoft-capture-vision-std/', dip: 'assets/dynamsoft-image-processing/', core: 'assets/dynamsoft-core/', license: 'assets/dynamsoft-license/', cvr: 'assets/dynamsoft-capture-vision-router/', dbr: 'assets/dynamsoft-barcode-reader/', dce: 'assets/dynamsoft-camera-enhancer/', };
Step 2: Create Angular Components for Setting License Key and Managing License Status
In this project, we will provide an input element for users to set a valid license key. Once the license key passes validation, the status is shared globally among different components: barcode reader and barcode scanner.
-
Generate a component and a shared service:
ng generate component product-list ng generate service shared
-
Update the shared service to store the boolean value in the shared.service.ts file:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class SharedService { private showDiv: boolean = false; getShowDiv(): boolean { return this.showDiv; } setShowDiv(value: boolean): void { this.showDiv = value; } toggleShowDiv(): void { this.showDiv = !this.showDiv; } }
-
Update the component to use the shared service in the product-list.component.ts file:
import { Component } from '@angular/core'; import { products } from '../products'; import { CoreModule, LicenseManager } from 'dynamsoft-barcode-reader-bundle'; import { SharedService } from '../shared.service'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.css'], }) export class ProductListComponent { products = products; inputText: string = ''; processedText: string = ''; placeholderText: string = 'DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=='; constructor(private sharedService: SharedService) { } async activate(): Promise<void> { this.processedText = this.inputText.toUpperCase(); // Configure the paths where the .wasm files and other necessary resources for modules are located. CoreModule.engineResourcePaths = { std: 'assets/dynamsoft-capture-vision-std/', dip: 'assets/dynamsoft-image-processing/', core: 'assets/dynamsoft-core/', license: 'assets/dynamsoft-license/', cvr: 'assets/dynamsoft-capture-vision-router/', dbr: 'assets/dynamsoft-barcode-reader/', dce: 'assets/dynamsoft-camera-enhancer/', }; try { // Visit https://www.dynamsoft.com/customer/license/trialLicense?utm_source=github&product=dbr to get a trial license. let licenseKey: string = this.inputText === '' ? this.placeholderText : this.inputText; await LicenseManager.initLicense(licenseKey, true); CoreModule.loadWasm(['DBR']); this.toggleDivVisibility(); } catch (error) { alert(error); } } toggleDivVisibility(): void { this.sharedService.toggleShowDiv(); } get showDiv(): boolean { return this.sharedService.getShowDiv(); } }
Explanation:
- The
activate
method is triggered when the user clicks theActivate
button. It initializes the Dynamsoft JavaScript Barcode SDK with the license key. - The license key stored in the placeholder text is a one-day trial license key publicly available for all users.
- The
showDiv
property is used to toggle the visibility of barcode reader and scanner components after the license key is validated.
- The
-
Update the component template in the product-list.component.html file:
<div> <div> Get a License key from <a href="https://www.dynamsoft.com/customer/license/trialLicense" target="_blank">here</a> </div> <input type="text" [(ngModel)]="inputText" [placeholder]="placeholderText"> <button (click)="activate()">Activate SDK</button> </div> <div *ngIf="showDiv"> <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']">> </a> </div> <ng-template #elseBlock><a [title]="product.name + ' details'" [routerLink]="['/barcode-scanner']">> </a></ng-template> </h3> <p *ngIf="product.description">Description: </p> </div> </div>
Explanation:
- The
input
element is used to set the license key. - The
button
element is used to trigger theactivate
method. - The
*ngIf
directive is used to toggle the visibility of the barcode reader and scanner components.
- The
Step3: Implement Angular Barcode Reader
-
Create a
barcode-reader
component via Angular CLI:ng generate component barcode-reader
-
Construct the web page layout in the
barcode-reader.component.html
file:<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>
Explanation
- The
input
element is used to select an image file. - The
a
element is used to display barcode and QR code results. - The
img
element is used to display the selected image. - The
canvas
element is used to draw barcode and QR code results.
- The
-
Add TypeScript code to the
barcode-reader.component.ts
file.import { Component, OnInit } from '@angular/core'; import { OverlayManager } from '../overlay'; import { CapturedResult, CaptureVisionRouter, BarcodeResultItem, EnumCapturedResultItemType } from 'dynamsoft-barcode-reader-bundle'; @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; overlayManager: OverlayManager; cvr: CaptureVisionRouter | undefined; constructor() { this.overlayManager = new OverlayManager(); } ngOnInit(): void { this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement); (async () => { this.cvr = await CaptureVisionRouter.createInstance(); this.isLoaded = true; })(); } 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.overlayManager.updateOverlay(img.width, img.height); if (this.cvr) { this.cvr.capture(file, 'ReadBarcodes_Balance').then((result: CapturedResult) => { console.log(result); let txts: any = []; let elem = document.getElementById('result'); try { let localization; let items = result.items if (items.length > 0) { for (var i = 0; i < items.length; ++i) { if (items[i].type !== EnumCapturedResultItemType.CRIT_BARCODE) { continue; // check if captured result item is a barcode } let item = items[i] as BarcodeResultItem; txts.push(item.text); localization = item.location; console.log(localization); this.overlayManager.drawOverlay( localization, item.text ); } 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); } } } }
Explanation
- The
ngOnInit
method initializes the overlay and CaptureVisionRouter object. - The
onChange
method is triggered when the user selects an image file. It reads the barcode and QR code results from the image file.
- The
Step4: Implement Angular Barcode Scanner
-
Create a
barcode-scanner
component via Angular CLI:ng generate component barcode-scanner
-
Construct the web page layout in the
barcode-scanner.component.html
file:<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>
Explanation
- The
select
element is used to list the available video sources. - The
div
element with thevideoContainer
id is used to display the video stream. - The
canvas
element with theoverlay
id is used to draw barcode and QR code results.
- The
-
Add TypeScript code to the
barcode-scanner.component.ts
file:import { Component, OnInit } from '@angular/core'; import { OverlayManager } from '../overlay'; import { BarcodeResultItem, CameraEnhancer, CameraView, CapturedResult, CaptureVisionRouter, EnumCapturedResultItemType, MultiFrameResultCrossFilter, Resolution } from 'dynamsoft-barcode-reader-bundle'; const componentDestroyedErrorMsg = 'VideoCapture Component Destroyed'; @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; cameraInfo: any = {}; videoSelect: HTMLSelectElement | undefined; overlayManager: OverlayManager; cvr?: CaptureVisionRouter; cameraEnhancer?: CameraEnhancer; isDestroyed = false; constructor() { this.overlayManager = new OverlayManager(); } ngOnInit(): void { this.videoSelect = document.querySelector('select#videoSource') as HTMLSelectElement; this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement); (async () => { await this.initBarcodeScanner(); })(); } ngOnDestroy(): void { this.isDestroyed = true; try { this.cvr?.dispose(); this.cameraEnhancer?.dispose(); } catch (_) { } } updateResolution(): void { if (this.cameraEnhancer && this.overlayManager) { let resolution: Resolution = this.cameraEnhancer.getResolution(); this.overlayManager.updateOverlay(resolution.width, resolution.height); } } async initBarcodeScanner(): Promise<void> { const cameraView: CameraView = await CameraView.createInstance(); this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView); this.isLoaded = true; let uiElement = document.getElementById('videoContainer'); if (uiElement) { uiElement.append(cameraView.getUIElement()); cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-camera')?.setAttribute('style', 'display: none'); cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-resolution')?.setAttribute('style', 'display: none'); let cameras = await this.cameraEnhancer.getAllCameras(); this.listCameras(cameras); this.cvr = await CaptureVisionRouter.createInstance(); if (this.isDestroyed) { throw Error(componentDestroyedErrorMsg); } this.cvr.setInput(this.cameraEnhancer); // Define a callback for results. this.cvr.addResultReceiver({ onCapturedResultReceived: (result: CapturedResult) => { this.overlayManager.clearOverlay(); let txts: any = []; let resultElement = document.getElementById('result'); try { let localization; let items = result.items if (items.length > 0) { for (var i = 0; i < items.length; ++i) { if (items[i].type !== EnumCapturedResultItemType.CRIT_BARCODE) { continue; // check if captured result item is a barcode } let item = items[i] as BarcodeResultItem; txts.push(item.text); localization = item.location; console.log(localization); this.overlayManager.drawOverlay( localization, item.text ); } if (resultElement) { resultElement.innerHTML = txts.join(', '); } } else { if (resultElement) { resultElement.innerHTML = txts.join(', '); } } } catch (e) { alert(e); } }, }); this.cvr.addResultReceiver({ onDecodedBarcodesReceived: (result) => { if (!result.barcodeResultItems.length) return; console.log(result); }, }); this.cameraEnhancer.on('played', () => { this.updateResolution(); }); await this.openCamera(); if (this.isDestroyed) { throw Error(componentDestroyedErrorMsg); } await this.cvr.startCapturing('ReadSingleBarcode'); if (this.isDestroyed) { throw Error(componentDestroyedErrorMsg); } } } async openCamera(): Promise<void> { this.overlayManager.clearOverlay(); if (this.videoSelect) { let deviceId = this.videoSelect.value; if (this.cameraEnhancer) { await this.cameraEnhancer.selectCamera(deviceId); await this.cameraEnhancer.open(); } } } listCameras(deviceInfos: any): void { for (var i = 0; i < deviceInfos.length; ++i) { var deviceInfo = deviceInfos[i]; var option = document.createElement('option'); option.value = deviceInfo.deviceId; option.text = deviceInfo.label; this.cameraInfo[deviceInfo.deviceId] = deviceInfo; if (this.videoSelect) this.videoSelect.appendChild(option); } } }
Explanation:
- The
ngOnInit
method initializes the overlay,CameraView
,CaptureVisionRouter
andCameraEnhancer
objects. - The
updateResolution
method updates the overlay size according to the camera resolution. - The
openCamera
opens the selected camera source. - The
listCameras
method lists the available camera sources. - The
ngOnDestroy
method destroys theCaptureVisionRouter
andCameraEnhancer
objects when the component is destroyed.
- The
-
Run the Angular project:
ng serve --ssl
Step 5: GitHub Page Deployment
Deploying your Angular project to GitHub Pages can be automated using GitHub Actions. Follow these steps to set up the deployment process:
-
Create a GitHub Actions workflow file to automate the build and deployment process. This workflow uses the Angular Deploy gh-pages Actions:
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: $ build_configuration: production base_href: /angular-barcode-qr-code-scanner/ deploy_branch: gh-pages angular_dist_build_folder: dist/angular-barcode-qr-code-scanner
Note: Replace
angular-barcode-qr-code-scanner
with your actual project name. -
After setting up the workflow file, enable GitHub Pages for your repository:
- Go to
Your GitHub repository > Settings > Pages
. - Select the
gh-pages
branch as the source for GitHub Pages. - Save the changes to launch the GitHub page.
- Go to
Q&A
Q: What if you encounter the following error on Windows?
ng : File C:\Users\Admin\AppData\Roaming\npm\ng.ps1 cannot be loaded because running scripts is disabled on this system
. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
A: The workaround is to temporarily bypass the execution policy for the current PowerShell session by running the following command:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Source Code
https://github.com/yushulx/angular-barcode-mrz-document-scanner