Input Masks
Tess UI Library fornisce utilità per applicare maschere di input per formattazione automatica dei dati inseriti dall'utente.
Panoramica
Le input masks migliorano l'esperienza utente fornendo:
- ✅ Formattazione automatica: Applica pattern durante la digitazione
- ✅ Validazione visiva: Feedback immediato sul formato
- ✅ Prevenzione errori: Limita input a caratteri validi
- ✅ Localizzazione: Supporto per formati locali
Masks Disponibili
Currency Mask
Formatta valori monetari.
import { CurrencyMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [CurrencyMaskDirective, ReactiveFormsModule],
template: `
<input
type="text"
[formControl]="priceControl"
tessCurrencyMask
[locale]="'it-IT'"
[currency]="'EUR'"
/>
`,
})
export class ProductFormComponent {
priceControl = new FormControl(1234.56);
}
// Display: € 1.234,56
Opzioni
locale: Locale per formattazione (default: 'it-IT')currency: Codice valuta ISO (default: 'EUR')precision: Decimali (default: 2)
Phone Mask
Formatta numeri di telefono.
import { PhoneMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [PhoneMaskDirective, ReactiveFormsModule],
template: `
<input
type="tel"
[formControl]="phoneControl"
tessPhoneMask
[countryCode]="'IT'"
/>
`,
})
export class ContactFormComponent {
phoneControl = new FormControl('');
}
// Input: 3331234567
// Display: +39 333 123 4567
Opzioni
countryCode: Codice paese ISO (default: 'IT')format: Pattern personalizzato
Date Mask
Formatta date.
import { DateMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [DateMaskDirective, ReactiveFormsModule],
template: `
<input
type="text"
[formControl]="dateControl"
tessDateMask
[format]="'dd/MM/yyyy'"
/>
`,
})
export class EventFormComponent {
dateControl = new FormControl('');
}
// Input: 08012026
// Display: 08/01/2026
Formati Supportati
dd/MM/yyyy(default)MM/dd/yyyyyyyy-MM-dd- Custom pattern
Credit Card Mask
Formatta numeri di carte di credito.
import { CreditCardMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [CreditCardMaskDirective, ReactiveFormsModule],
template: `
<input
type="text"
[formControl]="cardControl"
tessCreditCardMask
maxlength="19"
/>
`,
})
export class PaymentFormComponent {
cardControl = new FormControl('');
}
// Input: 1234567890123456
// Display: 1234 5678 9012 3456
VAT/Tax ID Mask
Formatta Partita IVA italiana.
import { VatMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [VatMaskDirective, ReactiveFormsModule],
template: `
<input
type="text"
[formControl]="vatControl"
tessVatMask
[country]="'IT'"
/>
`,
})
export class CompanyFormComponent {
vatControl = new FormControl('');
}
// Input: 12345678901
// Display: IT 12345678901
IBAN Mask
Formatta codici IBAN.
import { IbanMaskDirective } from '@tess-ui-library/shared';
@Component({
standalone: true,
imports: [IbanMaskDirective, ReactiveFormsModule],
template: `
<input
type="text"
[formControl]="ibanControl"
tessIbanMask
/>
`,
})
export class BankFormComponent {
ibanControl = new FormControl('');
}
// Input: IT60X0542811101000000123456
// Display: IT60 X054 2811 1010 0000 0012 3456
Custom Masks
Creare una Mask Custom
import { Directive, HostListener, ElementRef } from '@angular/core';
@Directive({
selector: '[appCustomMask]',
standalone: true,
})
export class CustomMaskDirective {
private el: HTMLInputElement;
constructor(private elementRef: ElementRef) {
this.el = this.elementRef.nativeElement;
}
@HostListener('input', ['$event'])
onInput(event: Event): void {
const input = event.target as HTMLInputElement;
let value = input.value;
// Rimuovi caratteri non validi
value = value.replace(/[^0-9]/g, '');
// Applica pattern personalizzato
// Esempio: XXX-XXX-XXX
if (value.length > 3) {
value = value.slice(0, 3) + '-' + value.slice(3);
}
if (value.length > 7) {
value = value.slice(0, 7) + '-' + value.slice(7, 10);
}
input.value = value;
}
@HostListener('blur')
onBlur(): void {
// Validazione finale al blur
if (this.el.value.length > 0 && this.el.value.length < 11) {
this.el.setCustomValidity('Formato non valido');
} else {
this.el.setCustomValidity('');
}
}
}
Pattern Generico
import { Directive, Input, HostListener, ElementRef } from '@angular/core';
@Directive({
selector: '[appPatternMask]',
standalone: true,
})
export class PatternMaskDirective {
@Input() pattern: string = ''; // es: '###-###-####'
@Input() placeholder: string = '#';
private el: HTMLInputElement;
constructor(private elementRef: ElementRef) {
this.el = this.elementRef.nativeElement;
}
@HostListener('input', ['$event'])
onInput(event: Event): void {
const input = event.target as HTMLInputElement;
let value = input.value.replace(/\D/g, '');
let formatted = '';
let valueIndex = 0;
for (let i = 0; i < this.pattern.length && valueIndex < value.length; i++) {
if (this.pattern[i] === this.placeholder) {
formatted += value[valueIndex];
valueIndex++;
} else {
formatted += this.pattern[i];
}
}
input.value = formatted;
}
}
// Uso
<input appPatternMask pattern="(###) ###-####" placeholder="#" />
// Input: 3331234567
// Display: (333) 123-4567
Integrazione con Form Validation
import { Component } from '@angular/core';
import { FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
import { PhoneMaskDirective } from '@tess-ui-library/shared';
import { phoneValidator } from '@tess-ui-library/core';
@Component({
standalone: true,
imports: [PhoneMaskDirective, ReactiveFormsModule],
template: `
<div class="field">
<label for="phone">Telefono</label>
<input
id="phone"
type="tel"
[formControl]="phoneControl"
tessPhoneMask
[countryCode]="'IT'"
/>
@if (phoneControl.errors?.['phone']) {
<small class="error">Numero di telefono non valido</small>
}
</div>
`,
})
export class ContactFormComponent {
phoneControl = new FormControl('', [
Validators.required,
phoneValidator,
]);
}
Best Practices
- Combina con validatori: Usa mask + validatori per garantire dati corretti
- Feedback visivo: Mostra errori di validazione sotto l'input
- Placeholder descrittivo: Indica il formato atteso
- Accessibilità: Usa
aria-labelearia-describedbyper screen reader - Mobile friendly: Usa
inputmodeetypeappropriati - Performance: Debounce per operazioni costose
- Testing: Testa con vari input e edge cases
Esempi Completi
Form di Pagamento
@Component({
standalone: true,
imports: [
ReactiveFormsModule,
CreditCardMaskDirective,
DateMaskDirective,
],
template: `
<form [formGroup]="paymentForm" (ngSubmit)="onSubmit()">
<div class="field">
<label>Numero Carta</label>
<input
formControlName="cardNumber"
tessCreditCardMask
placeholder="1234 5678 9012 3456"
inputmode="numeric"
/>
</div>
<div class="field">
<label>Scadenza</label>
<input
formControlName="expiryDate"
tessDateMask
[format]="'MM/yy'"
placeholder="MM/YY"
inputmode="numeric"
/>
</div>
<div class="field">
<label>CVV</label>
<input
formControlName="cvv"
type="password"
maxlength="3"
placeholder="123"
inputmode="numeric"
/>
</div>
<button type="submit" [disabled]="paymentForm.invalid">
Paga
</button>
</form>
`,
})
export class PaymentFormComponent {
paymentForm = new FormGroup({
cardNumber: new FormControl('', [
Validators.required,
Validators.minLength(19),
]),
expiryDate: new FormControl('', [
Validators.required,
Validators.pattern(/^\d{2}\/\d{2}$/),
]),
cvv: new FormControl('', [
Validators.required,
Validators.pattern(/^\d{3}$/),
]),
});
onSubmit() {
if (this.paymentForm.valid) {
console.log('Payment data:', this.paymentForm.value);
}
}
}