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

Ngx Mask与Ngx Formly设置初始值后标签不浮动问题

问题描述

我有一个基于Angular独立API的应用,使用ngx-formly构建表单,目前正在集成ngx-mask实现输入掩码功能。我创建了名为masked-input的自定义ngx-formly组件类型,该组件基于mat-form-field,包含mat-label和使用MatInput类的输入元素。

所有功能正常,直到通过defaultValue设置初始值时,出现标签与输入值重叠的问题(示例效果:标签覆盖在输入值上方,点击输入框后标签会恢复到正常的浮动位置)。

已尝试方案

  1. 变更检测调整:
    最初怀疑是变更检测问题,尝试将组件变更检测策略改为OnPush无明显效果;在组件生命周期多个阶段调用markForCheck()和detectChanges()也无效。仅在ngOnInit()中用setTimeout包裹markForCheck()(延迟0秒)能解决问题,但这是临时方案,不适用于正式发布。

  2. 输入属性调整:
    测试发现添加mask属性后问题出现;给输入框设置value=" "(空格)也能解决问题,但同样属于临时方案。

  3. 动画配置调整:
    在可复现示例中,移除bootstrapApplication()中的provideAnimations()调用后标签显示正常,但控制台会出现大量错误,这可能是排查线索。

代码与环境

依赖版本

"@angular-architects/module-federation": "^16.0.4",
"@angular/animations": "^16.2.0",
"@angular/common": "^16.2.0",
"@angular/compiler": "^16.2.0",
"@angular/core": "^16.2.0",
"@angular/forms": "^16.2.0",
"@angular/material": "^16.0.0",
"@angular/platform-browser": "^16.2.0",
"@angular/platform-browser-dynamic": "^16.2.0",
"@angular/router": "^16.2.0",
"@ngx-formly/core": "^6.1.8",
"@ngx-formly/material": "^6.2.2",
"ngx-mask": "^16.4.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.0"

使用更新版本也能复现该问题。

系统流程

  • 客户端向后端请求组件配置
  • 客户端通过自定义库根据配置创建表单组件、填充数据并应用掩码

Formly组件类型配置

{
  name: 'masked-input',
  component: MaskedInputTypeComponent,
  wrappers: ['form-field']
}

组件代码示例

@Component({
  selector: 'masked-input',
  standalone: true,
  imports: [
    MatInputModule,
    NgxMaskDirective,
    ReactiveFormsModule,
    FormlyModule,
    CommonModule
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <input
      matInput
      [formControl]="formControl"
      [formlyAttributes]="field"
      mask="{{this.mask}}"
      [patterns]="this.patterns ?? null"
      thousandSeparator="{{this.thousandSeparator ?? ' '}}"
      prefix="{{this.prefix ?? null}}"
      suffix="{{this.suffix ?? null}}"
      separatorLimit="{{this.separatorLimit ?? null}}"
      [hiddenInput]="this.hiddenInput ? this.hiddenInput : undefined"
      [dropSpecialCharacters]="this.dropSpecialCharacters ?? true"
      [showMaskTyped]="this.showMaskTyped ?? false"
      [allowNegativeNumbers]="this.allowNegativeNumbers ?? false"
      [placeHolderCharacter]="this.placeHolderCharacter ?? '_'"
      [clearIfNotMatch]="this.clearIfNotMatch ?? false"
      [leadZero]="this.leadZero ?? false"
      [leadZeroDateTime]="this.leadZeroDateTime ?? false"
      [specialCharacters]="this.specialCharacters ?? ['-','/','(',')','.',':',' ','+',',','@','[',']']"
    >
  `
})
export class MaskedInputTypeComponent
  extends FieldType<FieldTypeConfig> implements OnInit {
  label: string;
  mask: string | undefined;
  specialCharacters: string[] | undefined;
  dropSpecialCharacters: boolean | undefined;
  prefix: string | undefined;
  suffix: string | undefined;
  showMaskTyped: boolean | undefined;
  allowNegativeNumbers: boolean | undefined;
  placeHolderCharacter: string | undefined;
  clearIfNotMatch: boolean | undefined;
  thousandSeparator: string | undefined;
  leadZero: boolean | undefined;
  leadZeroDateTime: boolean | undefined;
  separatorLimit: number | undefined;
  hiddenInput: boolean | undefined;
  patterns: IMaskPatterns | undefined;

  ngOnInit(): void {
    const maskConfig: IMaskConfig | undefined = this.props['maskConfig'] as IMaskConfig | undefined;
    if (maskConfig) {
      this.mask = maskConfig.mask;
      this.specialCharacters = maskConfig.specialCharacters;
      this.dropSpecialCharacters = maskConfig.dropSpecialCharacters;
      this.prefix = maskConfig.prefix;
      this.suffix = maskConfig.suffix;
      this.showMaskTyped = maskConfig.showMaskTyped;
      this.allowNegativeNumbers = maskConfig.allowNegativeNumbers;
      this.placeHolderCharacter = maskConfig.placeHolderCharacter;
      this.clearIfNotMatch = maskConfig.clearIfNotMatch;
      this.thousandSeparator = maskConfig.thousandSeparator;
      this.leadZero = maskConfig.leadZero;
      this.leadZeroDateTime = maskConfig.leadZeroDateTime;
      this.separatorLimit = maskConfig.separatorLimit
      this.hiddenInput = maskConfig.hiddenInput;
      this.patterns = maskConfig.patterns as IMaskPatterns;
    }
    this.label = this.props['label'] as string;
  }
}

更新说明

已创建可复现该问题的示例。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:15:13