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
相关产品推荐
相关产品推荐

