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

Angular模板驱动表单:为重复mat-input字段添加唯一值验证

嘿,针对你在模板驱动表单里循环生成Angular Material输入框,要实现值不能重复的验证需求,我给你整理了两种可行方案,一步步来就能搞定:

方案一:自定义验证器指令(推荐,可复用)

如果这个重复值验证逻辑以后还要在其他组件用,写个自定义指令是最优解,步骤如下:

1. 生成并编写验证器指令

先通过Angular CLI生成指令:

ng generate directive unique-distance

然后打开生成的unique-distance.directive.ts,替换成下面的代码:

import { Directive, Input } from '@angular/core';
import { Validator, AbstractControl, ValidationErrors } from '@angular/forms';

@Directive({
  selector: '[appUniqueDistance]'
})
export class UniqueDistanceDirective implements Validator {
  // 接收整个距离数组
  @Input('appUniqueDistance') distanceArray: { distance: number }[] = [];
  // 接收当前输入框的索引,用来排除自身(避免和自己比较)
  @Input() currentIndex: number = 0;

  validate(control: AbstractControl): ValidationErrors | null {
    const currentValue = control.value;
    // 空值不触发重复验证,可根据需求调整
    if (!currentValue) return null;

    // 检查数组中除当前项外,是否有相同的距离值
    const isDuplicate = this.distanceArray.some((item, index) => {
      return index !== this.currentIndex && item.distance === currentValue;
    });

    return isDuplicate ? { duplicateDistance: true } : null;
  }
}

2. 在模板中应用指令

修改你现有的模板代码,给每个输入框绑定指令,同时传入数组和当前索引:

<div *ngFor="let distanceVal of distanceArray;let i=index" layout-gt-xs="row">
  <div flex="30" flex-offset="15">
    <mat-form-field>
      <input 
        matInput 
        placeholder="Distance" 
        type="number" 
        [(ngModel)]="distanceVal.distance" 
        name="distance{{i}}"
        appUniqueDistance 
        [distanceArray]="distanceArray"
        [currentIndex]="i"
        #distanceInput="ngModel"
      >
      <!-- 显示重复错误提示 -->
      <mat-error *ngIf="distanceInput.invalid && (distanceInput.dirty || distanceInput.touched)">
        <span *ngIf="distanceInput.hasError('duplicateDistance')">该距离值已存在,请输入不同的数值</span>
      </mat-error>
    </mat-form-field>
  </div>
</div>

这里的#distanceInput="ngModel"是模板引用变量,用来获取输入框的表单状态;mat-error会在输入框被触摸/修改且验证不通过时显示。

3. 在模块中注册指令

最后要把指令添加到对应模块的declarations(和exports,如果要在其他组件复用)里:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UniqueDistanceDirective } from './unique-distance.directive';
import { FormsModule } from '@angular/forms'; // 模板驱动表单必须引入
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

@NgModule({
  declarations: [UniqueDistanceDirective],
  imports: [
    CommonModule,
    FormsModule,
    MatFormFieldModule,
    MatInputModule
  ],
  exports: [UniqueDistanceDirective] // 要复用的话记得导出
})
export class YourFeatureModule { }
方案二:组件内直接验证(快速实现,仅当前组件可用)

如果这个验证逻辑只在当前组件用,不想写指令,可以直接在组件类里写验证方法:

1. 组件类添加验证方法

import { Component } from '@angular/core';

@Component({
  selector: 'app-your-component',
  templateUrl: './your-component.component.html',
  styleUrls: ['./your-component.component.css']
})
export class YourComponent {
  distanceArray: { distance: number }[] = [/* 初始化你的数组数据 */];

  // 检查当前索引的距离值是否重复
  isDistanceDuplicate(index: number): boolean {
    const currentValue = this.distanceArray[index].distance;
    if (!currentValue) return false;
    return this.distanceArray.some((item, i) => i !== index && item.distance === currentValue);
  }
}

2. 模板中调用验证方法

修改模板里的mat-error部分:

<div *ngFor="let distanceVal of distanceArray;let i=index" layout-gt-xs="row">
  <div flex="30" flex-offset="15">
    <mat-form-field>
      <input 
        matInput 
        placeholder="Distance" 
        type="number" 
        [(ngModel)]="distanceVal.distance" 
        name="distance{{i}}"
        #distanceInput="ngModel"
      >
      <mat-error *ngIf="distanceInput.dirty && isDistanceDuplicate(i)">
        该距离值已存在,请输入不同的数值
      </mat-error>
    </mat-form-field>
  </div>
</div>

额外注意事项

  • 当数组添加/删除项时,两种方案都会自动重新验证,因为数据绑定会触发更新。
  • 空值的处理逻辑可以根据你的需求调整,比如如果要求必须输入值,可以结合required验证一起使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:28:36