You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular(TypeScript)中复刻不修改模型的美国手机号掩码指令

How to implement a US phone number mask in Angular (TypeScript) with unformatted model value

Got it, I've tackled this exact scenario before—you want the input to show the clean masked phone number, but keep the raw 10-digit number in your model. The key here is leveraging Angular's ControlValueAccessor interface, which lets you fully separate what's displayed in the view from what's stored in the underlying model.

Step 1: Create a Phone Mask Directive

This directive will handle syncing the masked view value with the raw model value, just like your AngularJS implementation did. We'll hook into Angular's form control system using ControlValueAccessor:

import { Directive, ElementRef, HostListener, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Directive({
  selector: '[usPhoneMask]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => UsPhoneMaskDirective),
      multi: true
    }
  ]
})
export class UsPhoneMaskDirective implements ControlValueAccessor {
  private onChange: (value: string) => void = () => {};
  private onTouched: () => void = () => {};
  private rawValue = '';

  constructor(private el: ElementRef) {}

  // Sync model value to view (format raw digits for display)
  writeValue(value: string): void {
    this.rawValue = value || '';
    this.el.nativeElement.value = this.formatPhoneNumber(this.rawValue);
  }

  // Register callback to update model when view changes
  registerOnChange(fn: (value: string) => void): void {
    this.onChange = fn;
  }

  // Register callback for touch events (like blur)
  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  // Handle user input: clean non-digits, update view, sync raw value to model
  @HostListener('input', ['$event.target.value'])
  onInput(value: string): void {
    // Extract only digits and limit to 10 characters
    const raw = value.replace(/\D/g, '').slice(0, 10);
    this.rawValue = raw;
    // Update input with masked format
    this.el.nativeElement.value = this.formatPhoneNumber(raw);
    // Send raw digits back to the model
    this.onChange(raw);
  }

  @HostListener('blur')
  onBlur(): void {
    this.onTouched();
  }

  // Helper to convert raw digits to (xxx) xxx-xxxx format
  private formatPhoneNumber(raw: string): string {
    if (!raw) return '';
    
    const areaCode = raw.slice(0, 3);
    const middleSegment = raw.slice(3, 6);
    const lastSegment = raw.slice(6, 10);

    let formatted = '';
    if (areaCode) formatted += `(${areaCode}`;
    if (middleSegment) formatted += `) ${middleSegment}`;
    if (lastSegment) formatted += `-${lastSegment}`;
    
    return formatted;
  }
}

Step 2: (Optional) Create a Reusable Phone Format Pipe

If you want to display the masked number elsewhere in your template (not just the input), a pipe makes this formatting reusable across your app:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'usPhone' })
export class UsPhonePipe implements PipeTransform {
  transform(raw: string): string {
    if (!raw) return '';
    
    const areaCode = raw.slice(0, 3);
    const middleSegment = raw.slice(3, 6);
    const lastSegment = raw.slice(6, 10);

    let formatted = '';
    if (areaCode) formatted += `(${areaCode}`;
    if (middleSegment) formatted += `) ${middleSegment}`;
    if (lastSegment) formatted += `-${lastSegment}`;
    
    return formatted;
  }
}

Step 3: Use the Directive in Your Template

Now you can apply the directive to your input, and bind it to your model. The input will show the masked format, while your model stays as a clean string of digits:

<div class="container">
  <label for="phoneInput">US Phone Number</label>
  <input 
    type="text" 
    id="phoneInput" 
    placeholder="(123) 456-7890"
    [(ngModel)]="phoneNumber" 
    usPhoneMask
  >
  <p>Raw Model Value: {{ phoneNumber }}</p>
  <!-- Optional: Use the pipe to display formatted number elsewhere -->
  <p>Formatted Number: {{ phoneNumber | usPhone }}</p>
</div>

How This Works

  • writeValue: When the model updates (e.g., from code), we take the raw digits and format them for the input view.
  • onInput: When the user types, we strip all non-digit characters, update the input with the masked format, and send only the raw digits back to the model.
  • ControlValueAccessor: This interface acts as the bridge between Angular's form controls and our custom directive—we're explicitly telling Angular how to handle view-model sync, which keeps the model value unpolluted by formatting characters.

This approach mirrors your original AngularJS functionality perfectly, but adapted for Angular's modern form system.

内容的提问来源于stack exchange,提问作者dota2pro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 01:52:37