How to Build an Angular Barcode & QR Code Detection App: A Step-by-Step Guide
Dynamsoft Capture Vision SDK consists of Dynamsoft Barcode Reader, Dynamsoft Label Recognizer, Dynamsoft Camera Enhancer, Dynamsoft Document Normalizer, and Dynamsoft Code Parser. In this article, we will demonstrate how to build Angular barcode and QR code scanner apps from scratch using the Dynamsoft Capture Vision SDK.
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 Capture Vision.
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-capture-vision-bundle dynamsoft-capture-vision-data
- dynamsoft-capture-vision-bundle: The package contains the core library of Dynamsoft Capture Vision.
- dynamsoft-capture-vision-data: The package provides model files for Dynamsoft Capture Vision.
Configure Angular to Use Third-Party JavaScript Libraries
To make the third-party JavaScript libraries work in Angular, follow these steps:
-
Open the
angular.jsonfile and add the following configuration under thebuildsection:"build": { "builder": "@angular-devkit/build-angular:browser", "options": { ... "assets": [ "src/favicon.ico", "src/assets", { "glob": "**/*", "input": "./node_modules/dynamsoft-capture-vision-bundle/dist", "output": "assets/dynamsoft-capture-vision-bundle" }, { "glob": "**/*", "input": "./node_modules/dynamsoft-capture-vision-data", "output": "assets/dynamsoft-capture-vision-data" }, ... ], }, ... }, -
In your TypeScript code, set the resource output paths as follows:
import { CoreModule, LicenseManager } from 'dynamsoft-capture-vision-bundle'; CoreModule.engineResourcePaths = { dcvBundle: getFullUrl('assets/dynamsoft-capture-vision-bundle/'), dcvData: getFullUrl('assets/dynamsoft-capture-vision-data/'), };
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-capture-vision-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 = 'LICENSE-KEY'; 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 = { dcvBundle: getFullUrl('assets/dynamsoft-capture-vision-bundle/'), dcvData: getFullUrl('assets/dynamsoft-capture-vision-data/'), }; try { // Visit https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform let licenseKey: string = this.inputText === '' ? this.placeholderText : this.inputText; await LicenseManager.initLicense(licenseKey, { executeNow: true }); CoreModule.loadWasm(['DBR']); this.toggleDivVisibility(); } catch (error) { alert(error); } } toggleDivVisibility(): void { this.sharedService.toggleShowDiv(); } get showDiv(): boolean { return this.sharedService.getShowDiv(); } }Explanation:
- The
activatemethod is triggered when the user clicks theActivatebutton. 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
showDivproperty 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/?product=dcv&package=cross-platform" 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
inputelement is used to set the license key. - The
buttonelement is used to trigger theactivatemethod. - The
*ngIfdirective is used to toggle the visibility of the barcode reader and scanner components.
- The
Step3: Implement Angular Barcode Reader
-
Create a
barcode-readercomponent via Angular CLI:ng generate component barcode-reader -
Construct the web page layout in the
barcode-reader.component.htmlfile:<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
inputelement is used to select an image file. - The
aelement is used to display barcode and QR code results. - The
imgelement is used to display the selected image. - The
canvaselement is used to draw barcode and QR code results.
- The
-
Add TypeScript code to the
barcode-reader.component.tsfile.import { Component, OnInit } from '@angular/core'; import { OverlayManager } from '../overlay'; import { CapturedResult, CaptureVisionRouter, BarcodeResultItem, EnumCapturedResultItemType } from 'dynamsoft-capture-vision-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
ngOnInitmethod initializes the overlay and CaptureVisionRouter object. - The
onChangemethod 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-scannercomponent via Angular CLI:ng generate component barcode-scanner -
Construct the web page layout in the
barcode-scanner.component.htmlfile:<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
selectelement is used to list the available video sources. - The
divelement with thevideoContainerid is used to display the video stream. - The
canvaselement with theoverlayid is used to draw barcode and QR code results.
- The
-
Add TypeScript code to the
barcode-scanner.component.tsfile:import { Component, OnInit } from '@angular/core'; import { OverlayManager } from '../overlay'; import { BarcodeResultItem, CameraEnhancer, CameraView, CapturedResult, CaptureVisionRouter, EnumCapturedResultItemType, MultiFrameResultCrossFilter, Resolution } from 'dynamsoft-capture-vision-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
ngOnInitmethod initializes the overlay,CameraView,CaptureVisionRouterandCameraEnhancerobjects. - The
updateResolutionmethod updates the overlay size according to the camera resolution. - The
openCameraopens the selected camera source. - The
listCamerasmethod lists the available camera sources. - The
ngOnDestroymethod destroys theCaptureVisionRouterandCameraEnhancerobjects 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-scannerNote: Replace
angular-barcode-qr-code-scannerwith 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-pagesbranch 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