Files
Lorenz Hilpert 2b5da00249 feat(checkout): add reward order confirmation feature with schema migrations
- Add new reward-order-confirmation feature library with components and store
- Implement checkout completion orchestrator service for order finalization
- Migrate checkout/oms/crm models to Zod schemas for better type safety
- Add order creation facade and display order schemas
- Update shopping cart facade with order completion flow
- Add comprehensive tests for shopping cart facade
- Update routing to include order confirmation page
2025-10-21 14:28:52 +02:00
..

@isa/utils/ean-validation

Lightweight Angular utility library for validating EAN (European Article Number) barcodes with reactive forms integration and standalone validation functions.

Overview

The EAN Validation library provides tools for validating 13-digit EAN barcodes in Angular applications. It includes both an Angular Forms validator for reactive form validation and a standalone utility function for programmatic validation. The library uses a simple regex-based approach for fast, efficient validation without external dependencies.

Table of Contents

Features

  • Angular Forms validator - eanValidator for reactive form controls
  • Standalone validation - isEan() utility function for programmatic checks
  • Regex-based validation - Fast, efficient 13-digit EAN validation
  • Zero dependencies - No external libraries required
  • Type-safe - Full TypeScript support
  • Null-safe - Graceful handling of undefined/null values
  • Lightweight - Minimal bundle size impact
  • Framework agnostic core - Validation logic can be used outside Angular

Quick Start

1. Reactive Form Validation

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { eanValidator } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-product-form',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="productForm">
      <label>
        EAN:
        <input formControlName="ean" type="text" />
      </label>

      @if (eanControl.errors?.['invalidEan']) {
        <div class="error">
          Please enter a valid 13-digit EAN
        </div>
      }
    </form>
  `
})
export class ProductFormComponent {
  productForm = new FormGroup({
    ean: new FormControl('', [eanValidator])
  });

  get eanControl() {
    return this.productForm.get('ean')!;
  }
}

2. Programmatic Validation

import { Component } from '@angular/core';
import { isEan } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-barcode-scanner',
  template: `
    <button (click)="validateBarcode('1234567890123')">
      Validate Barcode
    </button>
  `
})
export class BarcodeScannerComponent {
  validateBarcode(code: string): void {
    if (isEan(code)) {
      console.log('Valid EAN:', code);
      // Process valid EAN
    } else {
      console.error('Invalid EAN:', code);
      // Handle invalid EAN
    }
  }
}

3. Combined Usage

import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { eanValidator, isEan } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-product-search',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <input
      [formControl]="searchControl"
      placeholder="Enter EAN"
      (keyup.enter)="search()"
    />

    <button (click)="search()" [disabled]="!canSearch()">
      Search
    </button>
  `
})
export class ProductSearchComponent {
  searchControl = new FormControl('', [eanValidator]);

  canSearch(): boolean {
    const value = this.searchControl.value;
    return !!value && isEan(value);
  }

  search(): void {
    if (this.canSearch()) {
      const ean = this.searchControl.value!;
      console.log('Searching for:', ean);
      // Perform search
    }
  }
}

Core Concepts

EAN (European Article Number)

EAN is a 13-digit barcode standard used internationally for product identification:

1234567890123
└───┬────┘││└─ Check digit
    │     ││
    │     │└── Product code
    │     └─── Manufacturer code
    └───────── Country/GS1 prefix

Validation Rules

The library validates EANs based on these criteria:

  1. Length: Must be exactly 13 digits
  2. Content: Must contain only numeric characters (0-9)
  3. Format: No spaces, dashes, or other separators allowed

Valid EANs:

'1234567890123'  (13 digits, all numeric)
'0000000000000'  (13 digits, all numeric)
'9999999999999'  (13 digits, all numeric)

Invalid EANs:

'123456789012'    (12 digits - too short)
'12345678901234'  (14 digits - too long)
'123456789012A'   (contains letter)
'1234-5678-9012'  (contains dashes)
' 1234567890123'  (contains whitespace)
''                (empty string - via isEan())
undefined         (undefined - via isEan())

Regex Pattern

The validation uses a simple regex pattern:

const EAN_REGEX = /^[0-9]{13}$/;

Pattern breakdown:

  • ^ - Start of string
  • [0-9] - Any digit from 0 to 9
  • {13} - Exactly 13 times
  • $ - End of string

API Reference

eanValidator

Angular Forms validator function for validating EAN input.

Type: ValidatorFn

Signature:

eanValidator(control: AbstractControl): ValidationErrors | null

Parameters:

  • control: AbstractControl - The form control to validate

Returns:

  • null - If the value is valid or empty/null
  • { invalidEan: true } - If the value is invalid

Validation Behavior:

  • Empty values: Returns null (considered valid - use Validators.required for required fields)
  • Valid EAN: Returns null
  • Invalid EAN: Returns { invalidEan: true }

Example:

import { FormControl } from '@angular/forms';
import { eanValidator } from '@isa/utils/ean-validation';

const eanControl = new FormControl('', [eanValidator]);

eanControl.setValue('1234567890123');
console.log(eanControl.valid); // true

eanControl.setValue('123');
console.log(eanControl.errors); // { invalidEan: true }

eanControl.setValue('');
console.log(eanControl.valid); // true (empty is valid)

isEan

Standalone utility function for programmatic EAN validation.

Signature:

isEan(value: string | undefined): boolean

Parameters:

  • value: string | undefined - The value to validate

Returns:

  • true - If the value is a valid 13-digit EAN
  • false - If the value is invalid, undefined, null, or empty

Validation Behavior:

  • undefined/null: Returns false
  • Empty string: Returns false
  • Valid EAN: Returns true
  • Invalid EAN: Returns false

Example:

import { isEan } from '@isa/utils/ean-validation';

isEan('1234567890123'); // true
isEan('123');           // false
isEan('');              // false
isEan(undefined);       // false
isEan(null);            // false (TypeScript allows via type coercion)

EAN_REGEX

Regular expression constant for EAN validation.

Type: RegExp

Value: /^[0-9]{13}$/

Note: This is an internal constant. Prefer using eanValidator or isEan() for validation.

Example:

import { EAN_REGEX } from '@isa/utils/ean-validation';

// Direct regex usage (not recommended)
const isValid = EAN_REGEX.test('1234567890123');
console.log(isValid); // true

// Prefer using isEan() instead
const isValidPreferred = isEan('1234567890123');

Usage Examples

Basic Form Validation

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { eanValidator } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-product-form',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <div class="form-field">
        <label for="ean">Product EAN*</label>
        <input
          id="ean"
          formControlName="ean"
          type="text"
          maxlength="13"
          placeholder="1234567890123"
        />

        @if (ean.touched && ean.errors) {
          <div class="errors">
            @if (ean.errors['required']) {
              <p class="error">EAN is required</p>
            }
            @if (ean.errors['invalidEan']) {
              <p class="error">EAN must be exactly 13 digits</p>
            }
          </div>
        }
      </div>

      <button type="submit" [disabled]="!form.valid">
        Save Product
      </button>
    </form>
  `
})
export class ProductFormComponent {
  form = new FormGroup({
    ean: new FormControl('', [
      Validators.required,
      eanValidator
    ])
  });

  get ean() {
    return this.form.get('ean')!;
  }

  onSubmit(): void {
    if (this.form.valid) {
      const productData = this.form.value;
      console.log('Saving product:', productData);
      // Save logic
    }
  }
}

Barcode Scanner Integration

import { Component, inject, signal } from '@angular/core';
import { isEan } from '@isa/utils/ean-validation';

interface ScanResult {
  code: string;
  valid: boolean;
  timestamp: Date;
}

@Component({
  selector: 'app-barcode-scanner',
  template: `
    <div class="scanner">
      <h2>Barcode Scanner</h2>

      <button (click)="startScanning()">
        Start Scanning
      </button>

      <div class="scan-history">
        <h3>Scan History</h3>
        @for (scan of scanHistory(); track scan.timestamp) {
          <div
            class="scan-result"
            [class.valid]="scan.valid"
            [class.invalid]="!scan.valid"
          >
            <span>{{ scan.code }}</span>
            <span>{{ scan.valid ? '✓ Valid' : '✗ Invalid' }}</span>
            <span>{{ scan.timestamp | date: 'short' }}</span>
          </div>
        }
      </div>
    </div>
  `
})
export class BarcodeScannerComponent {
  scanHistory = signal<ScanResult[]>([]);

  startScanning(): void {
    // Simulated barcode scan
    this.processScan('1234567890123');
  }

  processScan(code: string): void {
    const valid = isEan(code);

    this.scanHistory.update(history => [
      {
        code,
        valid,
        timestamp: new Date()
      },
      ...history
    ]);

    if (valid) {
      console.log('Valid EAN scanned:', code);
      // Process valid EAN
      this.lookupProduct(code);
    } else {
      console.error('Invalid EAN scanned:', code);
      // Show error message
    }
  }

  lookupProduct(ean: string): void {
    // Product lookup logic
  }
}

Search with Validation

import { Component, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { eanValidator, isEan } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-product-search',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <div class="search-container">
      <input
        [formControl]="searchControl"
        type="text"
        placeholder="Enter 13-digit EAN"
        maxlength="13"
        (keyup.enter)="search()"
      />

      <button
        (click)="search()"
        [disabled]="!isValidEan()"
      >
        Search
      </button>

      @if (searchControl.touched && searchControl.errors?.['invalidEan']) {
        <p class="error">
          Please enter a valid 13-digit EAN
        </p>
      }

      @if (searchResult()) {
        <div class="result">
          <h3>Product Found</h3>
          <p>EAN: {{ searchResult()!.ean }}</p>
          <p>Name: {{ searchResult()!.name }}</p>
        </div>
      }

      @if (notFound()) {
        <p class="error">No product found with EAN: {{ lastSearch() }}</p>
      }
    </div>
  `
})
export class ProductSearchComponent {
  searchControl = new FormControl('', [eanValidator]);

  searchResult = signal<{ ean: string; name: string } | null>(null);
  notFound = signal(false);
  lastSearch = signal('');

  isValidEan(): boolean {
    const value = this.searchControl.value;
    return !!value && isEan(value);
  }

  search(): void {
    if (!this.isValidEan()) return;

    const ean = this.searchControl.value!;
    this.lastSearch.set(ean);

    // Simulated product lookup
    const product = this.mockProductLookup(ean);

    if (product) {
      this.searchResult.set(product);
      this.notFound.set(false);
    } else {
      this.searchResult.set(null);
      this.notFound.set(true);
    }
  }

  mockProductLookup(ean: string): { ean: string; name: string } | null {
    // Mock implementation
    return { ean, name: `Product ${ean}` };
  }
}

Batch Validation

import { Component, signal } from '@angular/core';
import { isEan } from '@isa/utils/ean-validation';

interface ValidationResult {
  ean: string;
  valid: boolean;
}

@Component({
  selector: 'app-batch-validator',
  template: `
    <div class="batch-validator">
      <h2>Batch EAN Validator</h2>

      <textarea
        [value]="inputEans()"
        (input)="inputEans.set($any($event.target).value)"
        placeholder="Enter EANs (one per line)"
        rows="10"
      ></textarea>

      <button (click)="validate()">Validate All</button>

      @if (results().length > 0) {
        <div class="results">
          <h3>Validation Results</h3>
          <p>
            Valid: {{ validCount() }} / {{ results().length }}
          </p>

          <table>
            <thead>
              <tr>
                <th>EAN</th>
                <th>Status</th>
              </tr>
            </thead>
            <tbody>
              @for (result of results(); track result.ean) {
                <tr [class.valid]="result.valid" [class.invalid]="!result.valid">
                  <td>{{ result.ean }}</td>
                  <td>{{ result.valid ? '✓ Valid' : '✗ Invalid' }}</td>
                </tr>
              }
            </tbody>
          </table>
        </div>
      }
    </div>
  `
})
export class BatchValidatorComponent {
  inputEans = signal('');
  results = signal<ValidationResult[]>([]);

  validCount = signal(0);

  validate(): void {
    const eans = this.inputEans()
      .split('\n')
      .map(line => line.trim())
      .filter(line => line.length > 0);

    const results: ValidationResult[] = eans.map(ean => ({
      ean,
      valid: isEan(ean)
    }));

    this.results.set(results);
    this.validCount.set(results.filter(r => r.valid).length);
  }
}

Dynamic Form with Multiple EANs

import { Component } from '@angular/core';
import { FormArray, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { eanValidator } from '@isa/utils/ean-validation';

@Component({
  selector: 'app-multi-ean-form',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <h3>Product EANs</h3>

      <div formArrayName="eans">
        @for (control of eanControls.controls; track $index) {
          <div class="ean-field">
            <input
              [formControlName]="$index"
              type="text"
              maxlength="13"
              placeholder="1234567890123"
            />

            @if (control.touched && control.errors?.['invalidEan']) {
              <span class="error">Invalid EAN</span>
            }

            <button type="button" (click)="removeEan($index)">
              Remove
            </button>
          </div>
        }
      </div>

      <button type="button" (click)="addEan()">
        Add EAN
      </button>

      <button type="submit" [disabled]="!form.valid">
        Save
      </button>
    </form>
  `
})
export class MultiEanFormComponent {
  form = new FormGroup({
    eans: new FormArray([
      new FormControl('', [eanValidator])
    ])
  });

  get eanControls() {
    return this.form.get('eans') as FormArray;
  }

  addEan(): void {
    this.eanControls.push(
      new FormControl('', [eanValidator])
    );
  }

  removeEan(index: number): void {
    this.eanControls.removeAt(index);
  }

  onSubmit(): void {
    if (this.form.valid) {
      const eans = this.eanControls.value;
      console.log('Saving EANs:', eans);
    }
  }
}

Type Guard Usage

import { Component } from '@angular/core';
import { isEan } from '@isa/utils/ean-validation';

interface Product {
  id: number;
  name: string;
  ean?: string; // Optional EAN
}

@Component({
  selector: 'app-product-list',
  template: `
    <div class="products">
      @for (product of products; track product.id) {
        <div class="product">
          <h3>{{ product.name }}</h3>

          @if (hasValidEan(product)) {
            <p>EAN: {{ product.ean }}</p>
          } @else {
            <p class="warning">No valid EAN</p>
          }
        </div>
      }
    </div>
  `
})
export class ProductListComponent {
  products: Product[] = [
    { id: 1, name: 'Product A', ean: '1234567890123' },
    { id: 2, name: 'Product B', ean: '123' }, // Invalid
    { id: 3, name: 'Product C' }, // Missing
  ];

  hasValidEan(product: Product): boolean {
    return isEan(product.ean);
  }
}

EAN Format

Standard Format

EAN-13 Structure:

┌──────────────────────────────────┐
│     1  2  3  4  5  6  7  8  9  0  1  2  3     │
│     └──┬──┘  └───────┬────────┘  │     │     │
│        │             │            │     │     │
│     Prefix      Manufacturer    Product Check │
│                  Code            Code   Digit │
└──────────────────────────────────┘

Common Prefixes

Prefix Region/Type
000-019 GS1 US
020-029 Restricted distribution
030-039 GS1 US
040-049 Restricted distribution
050-059 GS1 US reserved
060-139 GS1 US
200-299 Restricted distribution
300-379 GS1 France
380 GS1 Bulgaria
383 GS1 Slovenia
385 GS1 Croatia
400-440 GS1 Germany
450-459, 490-499 GS1 Japan
460-469 GS1 Russia
470 GS1 Kyrgyzstan
471 GS1 Taiwan
474 GS1 Estonia
475 GS1 Latvia
476 GS1 Azerbaijan
477 GS1 Lithuania
478 GS1 Uzbekistan
479 GS1 Sri Lanka
480 GS1 Philippines
481 GS1 Belarus
482 GS1 Ukraine
484 GS1 Moldova
485 GS1 Armenia
486 GS1 Georgia
487 GS1 Kazakhstan
489 GS1 Hong Kong
500-509 GS1 UK
520-521 GS1 Association Greece
528 GS1 Lebanon
529 GS1 Cyprus
530 GS1 Albania
531 GS1 Macedonia
535 GS1 Malta
539 GS1 Ireland
540-549 GS1 Belgium & Luxembourg
560 GS1 Portugal
569 GS1 Iceland
570-579 GS1 Denmark
590 GS1 Poland
594 GS1 Romania
599 GS1 Hungary
600-601 GS1 South Africa
603 GS1 Ghana
604 GS1 Senegal
608 GS1 Bahrain
609 GS1 Mauritius
611 GS1 Morocco
613 GS1 Algeria
615 GS1 Nigeria
616 GS1 Kenya
618 GS1 Ivory Coast
619 GS1 Tunisia
620 GS1 Tanzania
621 GS1 Syria
622 GS1 Egypt
623 GS1 Brunei
624 GS1 Libya
625 GS1 Jordan
626 GS1 Iran
627 GS1 Kuwait
628 GS1 Saudi Arabia
629 GS1 Emirates
640-649 GS1 Finland
690-699 GS1 China
700-709 GS1 Norway
729 GS1 Israel
730-739 GS1 Sweden
740 GS1 Guatemala
741 GS1 El Salvador
742 GS1 Honduras
743 GS1 Nicaragua
744 GS1 Costa Rica
745 GS1 Panama
746 GS1 Dominican Republic
750 GS1 Mexico
754-755 GS1 Canada
759 GS1 Venezuela
760-769 GS1 Switzerland
770-771 GS1 Colombia
773 GS1 Uruguay
775 GS1 Peru
777 GS1 Bolivia
778-779 GS1 Argentina
780 GS1 Chile
784 GS1 Paraguay
786 GS1 Ecuador
789-790 GS1 Brazil
800-839 GS1 Italy
840-849 GS1 Spain
850 GS1 Cuba
858 GS1 Slovakia
859 GS1 Czech
860 GS1 Serbia
865 GS1 Mongolia
867 GS1 North Korea
868-869 GS1 Turkey
870-879 GS1 Netherlands
880 GS1 South Korea
884 GS1 Cambodia
885 GS1 Thailand
888 GS1 Singapore
890 GS1 India
893 GS1 Vietnam
896 GS1 Pakistan
899 GS1 Indonesia
900-919 GS1 Austria
930-939 GS1 Australia
940-949 GS1 New Zealand
950 GS1 Global Office
951 GS1 Global Office (EPCglobal)
955 GS1 Malaysia
958 GS1 Macau
960-969 GS1 Global Office (GTIN-8)
977 Serial publications (ISSN)
978-979 Bookland (ISBN)
980 Refund receipts
981-984 GS1 coupon identification
990-999 GS1 coupon identification

Note: This library validates format only (13 digits), not check digit accuracy or prefix validity.

Architecture Notes

Design Patterns

Validator Function Pattern

The eanValidator follows Angular's ValidatorFn pattern:

export const eanValidator: ValidatorFn = (
  control: AbstractControl
): ValidationErrors | null => {
  const value = control.value;
  if (value && !EAN_REGEX.test(value)) {
    return { invalidEan: true };
  }
  return null;
};

Benefits:

  • Composable with other validators
  • Reusable across multiple form controls
  • Type-safe with TypeScript
  • Follows Angular conventions

Pure Function Pattern

The isEan function is a pure function with no side effects:

export const isEan = (value: string | undefined): boolean => {
  if (!value) {
    return false;
  }
  return EAN_REGEX.test(value);
};

Benefits:

  • Predictable - same input always produces same output
  • Testable - easy to unit test
  • Framework agnostic - can be used outside Angular
  • No dependencies - doesn't rely on external state

Performance Considerations

  1. Regex Performance - Simple regex pattern for O(n) validation
  2. No External Dependencies - Minimal bundle size impact
  3. Null-Safe - Early returns for undefined/null values
  4. Reusable Regex - Single compiled regex constant

Future Enhancements

Potential improvements identified:

  1. Check Digit Validation - Validate the EAN-13 check digit algorithm
  2. EAN-8 Support - Support for 8-digit EAN format
  3. UPC Support - Support for 12-digit UPC format
  4. Prefix Validation - Validate GS1 country/region prefixes
  5. Format Conversion - Convert between EAN-13, EAN-8, and UPC
  6. Barcode Generation - Generate barcode images from EAN
  7. Async Validation - Validate against product database

Testing

The library uses Vitest with Angular Testing Utilities for testing.

Running Tests

# Run tests for this library
npx nx test ean-validation --skip-nx-cache

# Run tests with coverage
npx nx test ean-validation --code-coverage --skip-nx-cache

# Run tests in watch mode
npx nx test ean-validation --watch

Test Structure

The library includes comprehensive unit tests covering:

  • eanValidator Tests:

    • Valid 13-digit EAN returns null
    • Empty value returns null (valid)
    • Invalid EAN returns error object
    • Null/undefined values handled correctly
  • isEan Tests:

    • Valid 13-digit EAN returns true
    • Invalid EAN returns false
    • Empty string returns false
    • Undefined returns false

Example Test

import { describe, it, expect } from 'vitest';
import { FormControl } from '@angular/forms';
import { eanValidator, isEan } from './ean-validation';

describe('eanValidator', () => {
  it('should return null for valid EAN', () => {
    // Arrange
    const control = new FormControl('1234567890123');

    // Act
    const result = eanValidator(control);

    // Assert
    expect(result).toBeNull();
  });

  it('should return null for empty value', () => {
    // Arrange
    const control = new FormControl('');

    // Act
    const result = eanValidator(control);

    // Assert
    expect(result).toBeNull();
  });

  it('should return error for invalid EAN', () => {
    // Arrange
    const control = new FormControl('123');

    // Act
    const result = eanValidator(control);

    // Assert
    expect(result).toEqual({ invalidEan: true });
  });
});

describe('isEan', () => {
  it('should return true for valid EAN', () => {
    // Arrange
    const validEan = '1234567890123';

    // Act
    const result = isEan(validEan);

    // Assert
    expect(result).toBe(true);
  });

  it('should return false for undefined value', () => {
    // Arrange
    const undefinedValue = undefined;

    // Act
    const result = isEan(undefinedValue);

    // Assert
    expect(result).toBe(false);
  });

  it('should return false for invalid EAN', () => {
    // Arrange
    const invalidEan = '123';

    // Act
    const result = isEan(invalidEan);

    // Assert
    expect(result).toBe(false);
  });
});

Dependencies

Required Libraries

  • @angular/core - For TypeScript types
  • @angular/forms - For ValidatorFn, AbstractControl, ValidationErrors types

Path Alias

Import from: @isa/utils/ean-validation

Exports

  • eanValidator - Angular Forms validator function
  • isEan - Standalone validation utility function
  • EAN_REGEX - Regular expression constant (internal use)

Best Practices

1. Use with Validators.required

Combine with Validators.required for required fields:

// Good - Required + EAN validation
const eanControl = new FormControl('', [
  Validators.required,
  eanValidator
]);

// Avoid - Only EAN validation (allows empty)
const eanControl = new FormControl('', [eanValidator]);

2. Provide Clear Error Messages

Show user-friendly error messages:

// Good - Clear, actionable error message
@if (ean.errors?.['invalidEan']) {
  <p class="error">
    Please enter a valid 13-digit EAN (e.g., 1234567890123)
  </p>
}

// Avoid - Generic or unclear message
@if (ean.errors?.['invalidEan']) {
  <p class="error">Invalid</p>
}

3. Use isEan for Type Guards

Use isEan() for runtime type checking:

// Good - Type-safe validation
function processEan(code: string | undefined): void {
  if (isEan(code)) {
    // TypeScript knows code is string here
    console.log('Processing:', code.toUpperCase());
  }
}

// Avoid - Unsafe type assumption
function processEan(code: string): void {
  // Might throw if code is invalid
  console.log('Processing:', code.toUpperCase());
}

4. Add Maxlength Attribute

Prevent users from entering more than 13 digits:

// Good - Limit input length
<input
  formControlName="ean"
  type="text"
  maxlength="13"
  placeholder="1234567890123"
/>

// Avoid - No length restriction
<input
  formControlName="ean"
  type="text"
/>

5. Handle Validation in Components

Keep validation logic in components, not templates:

// Good - Logic in component
canSubmit(): boolean {
  return this.form.valid && isEan(this.form.value.ean);
}

// Avoid - Complex logic in template
@if (form.valid && form.value.ean && /^[0-9]{13}$/.test(form.value.ean)) {
  ...
}

Limitations

What This Library Does NOT Do

  1. Check Digit Validation - Does not validate the EAN-13 checksum digit
  2. Database Validation - Does not verify if EAN exists in product database
  3. Prefix Validation - Does not validate GS1 country/region prefixes
  4. Format Conversion - Does not convert between EAN/UPC formats
  5. Barcode Generation - Does not generate barcode images

When to Use Additional Validation

For production systems, consider:

  • Check digit validation for data integrity
  • Database lookup for product existence
  • Prefix validation for regional compliance
  • Duplicate detection in batch processing

License

Internal ISA Frontend library - not for external distribution.