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

Angular响应式表单实现字段掩码显示与无掩码值存储的方法

在Angular响应式表单中实现“显示掩码+存储无掩码值”

我来帮你把模板驱动表单里的CNPJ掩码逻辑迁移到响应式表单中,核心思路是分离显示用的带掩码值和存储用的纯数字值,利用响应式表单的valueChanges监听控件变化,结合你的maskService来实现需求。下面给你两种可行的实现方案:

方案1:单FormControl手动处理掩码

这种方案只用一个FormControl存储纯数字值,手动生成带掩码的显示值,不需要依赖额外指令:

组件类代码

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { maskService } from './path-to-your-mask-service'; // 替换为你的掩码服务路径

@Component({
  selector: 'app-cnpj-form',
  templateUrl: './cnpj-form.component.html'
})
export class CnpjFormComponent implements OnInit {
  cnpjForm: FormGroup;
  // 专门用于存储带掩码的显示值
  displayedCnpj: string = '';

  constructor(private fb: FormBuilder) {
    // 表单的cnpj字段存储无掩码的纯数字
    this.cnpjForm = this.fb.group({
      cnpj: ['']
    });
  }

  ngOnInit(): void {
    // 监听表单控件值变化,同步更新显示值
    this.cnpjForm.get('cnpj')?.valueChanges.subscribe(rawValue => {
      if (!rawValue) {
        this.displayedCnpj = '';
        return;
      }
      // 确保存储的是纯数字(防止手动输入非数字字符)
      const unmaskedValue = maskService.unmask(rawValue);
      // 避免循环触发更新,仅当值不同时才同步
      if (unmaskedValue !== this.cnpjForm.get('cnpj')?.value) {
        this.cnpjForm.get('cnpj')?.setValue(unmaskedValue, { emitEvent: false });
      }
      // 生成带掩码的显示值
      this.displayedCnpj = this.applyMask(unmaskedValue, maskService.getCNPJ);
    });

    // 初始化:如果有初始的无掩码值,同步生成显示值
    const initialValue = this.cnpjForm.get('cnpj')?.value;
    if (initialValue) {
      this.displayedCnpj = this.applyMask(initialValue, maskService.getCNPJ);
    }
  }

  // 手动实现掩码应用逻辑(匹配你的maskService.getCNPJ规则)
  private applyMask(value: string, mask: Array<RegExp | string>): string {
    let maskedValue = '';
    let valueIndex = 0;
    for (const maskChar of mask) {
      if (valueIndex >= value.length) break;
      if (typeof maskChar === 'string') {
        // 掩码中的固定分隔符,直接添加
        maskedValue += maskChar;
      } else {
        // 匹配数字的正则,从输入值中取对应字符
        const char = value[valueIndex];
        if (maskChar.test(char)) {
          maskedValue += char;
          valueIndex++;
        }
      }
    }
    return maskedValue;
  }

  // 提交表单时,cnpj字段就是无掩码的纯数字
  onSubmit(): void {
    console.log('存储的无掩码CNPJ:', this.cnpjForm.value.cnpj);
    // 这里添加你的表单提交逻辑
  }

  // 处理输入框的输入事件,同步更新表单值和显示值
  onCnpjInput(event: Event): void {
    const inputElement = event.target as HTMLInputElement;
    const inputValue = inputElement.value;
    // 先对输入值去掩码得到纯数字
    const unmaskedValue = maskService.unmask(inputValue);
    // 更新表单存储的纯数字(关闭事件触发,避免循环)
    this.cnpjForm.get('cnpj')?.setValue(unmaskedValue, { emitEvent: false });
    // 更新显示的带掩码值
    this.displayedCnpj = this.applyMask(unmaskedValue, maskService.getCNPJ);
  }
}

模板代码

<form [formGroup]="cnpjForm" (ngSubmit)="onSubmit()">
  <input 
    type="tel" 
    class="form-control" 
    [value]="displayedCnpj" 
    (input)="onCnpjInput($event)"
    placeholder="Digite o CNPJ"
  >
  <button type="submit" class="mt-2">提交表单</button>
</form>

方案2:双FormControl复用textMask指令

这种方案更接近你原来的模板驱动写法,用两个FormControl分别处理显示(带掩码)和存储(纯数字),复用你熟悉的textMask指令:

组件类代码

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { maskService } from './path-to-your-mask-service';

@Component({
  selector: 'app-cnpj-form',
  templateUrl: './cnpj-form.component.html'
})
export class CnpjFormComponent implements OnInit {
  cnpjForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.cnpjForm = this.fb.group({
      cnpj: [''], // 存储无掩码的纯数字值
      cnpjDisplay: [''] // 显示带掩码的值
    });
  }

  ngOnInit(): void {
    // 监听显示控件的变化,同步更新存储的纯数字值
    this.cnpjForm.get('cnpjDisplay')?.valueChanges.subscribe(displayValue => {
      const unmaskedValue = maskService.unmask(displayValue);
      this.cnpjForm.get('cnpj')?.setValue(unmaskedValue, { emitEvent: false });
    });

    // 初始化:如果有初始纯数字值,同步生成带掩码的显示值
    const initialUnmasked = this.cnpjForm.get('cnpj')?.value;
    if (initialUnmasked) {
      const displayedValue = this.applyMask(initialUnmasked, maskService.getCNPJ);
      this.cnpjForm.get('cnpjDisplay')?.setValue(displayedValue, { emitEvent: false });
    }
  }

  // 复用方案1中的applyMask函数
  private applyMask(value: string, mask: Array<RegExp | string>): string {
    let maskedValue = '';
    let valueIndex = 0;
    for (const maskChar of mask) {
      if (valueIndex >= value.length) break;
      if (typeof maskChar === 'string') {
        maskedValue += maskChar;
      } else {
        const char = value[valueIndex];
        if (maskChar.test(char)) {
          maskedValue += char;
          valueIndex++;
        }
      }
    }
    return maskedValue;
  }

  onSubmit(): void {
    console.log('存储的无掩码CNPJ:', this.cnpjForm.value.cnpj);
    // 提交逻辑
  }
}

模板代码

<form [formGroup]="cnpjForm" (ngSubmit)="onSubmit()">
  <input 
    type="tel" 
    class="form-control" 
    formControlName="cnpjDisplay"
    [textMask]="{mask: maskService.getCNPJ, guide: false}"
    placeholder="Digite o CNPJ"
  >
  <button type="submit" class="mt-2">提交表单</button>
</form>

关键注意点

  • 无论哪种方案,更新表单值时都要加上{ emitEvent: false },避免触发valueChanges循环更新。
  • 如果你的项目中已经引入了angular2-text-mask库,方案2会更省心,不需要手动写applyMask函数。
  • 提交表单时,直接从cnpjForm.value.cnpj获取无掩码的纯数字值即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:06:22