directive:
import { Directive, HostListener, HostBinding, ElementRef } from '@angular/core'; @Directive({ selector: '[credit-card]' }) export class CreditCardDirective { @HostBinding('style.border') border: string; @HostListener('input', ['$event']) onKeyDown(event: KeyboardEvent) { const input = event.target as HTMLInputElement; let trimmed = input.value.replace(/\s+/g, ''); if (trimmed.length > 16) { trimmed = trimmed.substr(0, 16); } let numbers = []; for (let i = 0; i < trimmed.length; i += 4) { numbers.push(trimmed.substr(i, 4)); } input.value = numbers.join(' '); this.border = ''; if (/[^\d]+/.test(trimmed)) { this.border = '1px solid red'; } } }
test:
import { DebugElement, Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import { By } from '@angular/platform-browser'; import { CreditCardDirective } from './credit-card.directive'; TestBed.initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting() ); @Component({ template: ` <input type="text" [value]="value" credit-card> ` }) class TestComponent { value = 123456; } describe('CreditCardDirective', () => { let component: TestComponent; let fixture: ComponentFixture<TestComponent>; let el: DebugElement; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ CreditCardDirective, TestComponent ] }); fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; el = fixture.debugElement; }); it('should format the string with spaces', () => { const directive = el.query(By.directive(CreditCardDirective)).nativeElement; directive.value = '475123'; directive.dispatchEvent(new Event('input')); expect(directive.value).toBe('4751 23'); directive.value = '4751239812019201'; directive.dispatchEvent(new Event('input')); expect(directive.value).toBe('4751 2398 1201 9201'); }); it('should have a max-length of 16 characters', () => { const directive = el.query(By.directive(CreditCardDirective)); const directiveEl = directive.nativeElement; directiveEl.value = '4751239812019201998394282394823'; directiveEl.dispatchEvent(new Event('input')); expect(directiveEl.value).toBe('4751 2398 1201 9201'); directiveEl.value = 'abscdef'; directiveEl.dispatchEvent(new Event('input')); const directiveInstance = directive.injector.get(CreditCardDirective); expect(directiveInstance.border).toContain('1px solid red'); }); });