【问题标题】:Compare the values of 2 input fields to validate the form in Angular 7 through the template比较 2 个输入字段的值以通过模板验证 Angular 7 中的表单
【发布时间】:2019-12-12 23:57:44
【问题描述】:

我在迁移到 Angular 7 的第一周,我一直在使用基于模板的基本验证在我的项目中做基本表单,但我现在需要基于一个字段的值必须是验证表单比别人高

我尝试在组件控制器中使用这些值本身,但是虽然我能够确认这些值是否有效,但我无法使用此代码向用户展示问题所在

if (issueThresholdForm.value.lowScore > issueThresholdForm.value.highScore) {
  // Show user error
  // This is the messing part, I guess
}

这是我正在使用的模板

<div *ngIf="_issueCategory">
  <form (submit)="submitIssueThreshold(issueThresholdForm)" #issueThresholdForm="ngForm">
    <mat-form-field class="half-width" floatLabel="always">
      <mat-label [translate]="'issueThreshold.modals.highScore'"></mat-label>
      <input name="highScore" type="number" matInput placeholder="0" [(ngModel)]="_issueCategory.highScore"
        required #highScore="ngModel">
    </mat-form-field>
    <mat-form-field class="half-width" floatLabel="always">
      <mat-label [translate]="'issueThreshold.modals.lowScore'"></mat-label>
      <input name="lowScore" type="number" matInput placeholder="0" [(ngModel)]="_issueCategory.lowScore"
        required #lowScore="ngModel">
    </mat-form-field>
    <mat-form-field class="full-width" floatLabel="always">
      <mat-label [translate]="'issueThreshold.modals.description'"></mat-label>
      <textarea name="description" matInput [(ngModel)]="_issueCategory.thresholdDescription">
            </textarea>
    </mat-form-field>
    <div class="modal-footer">
      <button type="button" class="btn btn-secondary" data-dismiss="modal" [translate]="'modal-confirm.cancel'"></button>
      <button type="submit" class="btn btn-primary primary" [disabled]="issueThresholdForm.invalid || issueThresholdForm.pristine" [translate]="'modal-confirm.submit'"></button>
    </div>
  </form>
</div>

【问题讨论】:

  • 添加stackBlitz URL,很容易得到快速的答复
  • 不相关,但我建议转向反应式表单,使用自定义验证器轻松处理此类情况。当然你也可以在模板驱动的表单中做一个自定义验证器。
  • 响应式表单也有助于编写更好的单元测试

标签: angular typescript angular-validation


【解决方案1】:

编辑:

以反应方式使用相同的解决方案进行编辑。所以创建表单组并添加一个附加到表单组的自定义验证器:

_issueCategory = { lowScore: 1, highScore: 2 };

issueThresholdForm: FormGroup;

constructor(private fb: FormBuilder) {
  this.issueThresholdForm = this.fb.group({
    highScore: [this._issueCategory.highScore, [Validators.required]],
    lowScore: [this._issueCategory.lowScore, [Validators.required]]
  }, { validators: validateScore })
}

验证器函数:

export function validateScore(
  control: AbstractControl
): ValidationErrors | null {
  if (control && control.get("highScore") && control.get("lowScore")) {
    const highscore = control.get("highScore").value;
    const lowscore = control.get("lowScore").value;  
    return (lowscore > highscore) ? { scoreError: true } : null
  }
  return null;
}

然后您可以删除 ngModel(很重要!),因为它们不应该与反应形式混合。您还可以删除所有验证,如表单的required,因此最终输入看起来就像:

<input type="number" matInput placeholder="0" formControlName="lowScore">

STACKBLITZ


原文:

强烈,强烈建议Reactive forms,一开始他们可能会感到困惑,但完全值得。您可以更好地控制表单,正如 Nithin Kumar Biliya 在评论中提到的那样,单元测试更容易。

话虽这么说......

这是一个使用模板驱动表单的解决方案,因为这是您当前正在使用的。

您可以创建一个附加到表单标签的指令,并在该指令内部有一个验证器来比较高分和低分的值并将错误附加到表单,或返回null(在形式)。所以验证器看起来像这样:

import { Directive } from "@angular/core";
import {
  AbstractControl,
  NG_VALIDATORS,
  Validator,
  ValidationErrors
} from "@angular/forms";

@Directive({
  selector: "[scoreValidation]",
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: ScoreValidatorDirective,
      multi: true
    }
  ]
})
export class ScoreValidatorDirective implements Validator {
  constructor() {}

  // here control is the formgroup
  validate(control: AbstractControl): ValidationErrors | null {
    if (control && control.get("highScore") && control.get("lowScore")) {

      // the form controls and their value
      const highscore = control.get("highScore").value;
      const lowscore = control.get("lowScore").value;

      // not valid, return an error
      if (lowscore > highscore) {
        return { scoreError: true };
      }
      // valid
      return null;
    }
    // form controls do not exist yet, return null
    return null;
  }
}

将该指令添加到您的应用模块中的声明数组中,并通过将此指令附加到表单标签来使用它:

<form .... scoreValidation>

可以使用*ngIf="issueThresholdForm.hasError('scoreError')显示错误

STACKBLITZ

【讨论】:

  • 我同意,我已经阅读了有关响应式表单的信息,并且似乎可以完成我们需要完成的工作,但我不想为此创建一个全新的组件作为验证器,我猜这个随着验证者数量的增加,会变得相当复杂
  • 当然,我理解,响应式表单的相同方法要容易得多,也就是说,您只需将自定义验证器添加为函数,无需创建指令。我可以使用与反应式方法相同的选项来更新我的答案。
  • @user1712638,在那里,我在答案中添加了反应式方法。希望对您有所帮助,并且是您希望做的:)
【解决方案2】:

我强烈推荐响应式表单,但如果你想这样做,你可以:

将以下p tag 放在lowScore 输入下:

<p class="text-danger" [hidden]="(lowerScore.value > higerScore.value ? false: true) || (lowScore.pristine && !issueThresholdForm.submitted)">
                    The lower scrore can not be greater than higer score
</p>

【讨论】:

  • 不幸的是,这不会阻止提交表单或整个表单或用问题标记输入,我想我会使用反应式表单
【解决方案3】:

您可以在Reactive Forms 中使用Custom Validations,如下所示。

HTML

<div>
  <form [formGroup]="myForm">

    <label>Low Score: </label>
    <input formControlName="lowScore" type="number">
    <br/><br/>
    <label>High Score: </label>
    <input formControlName="highScore" type="number">

    <div>
      <span style="color: red" *ngIf="myForm.get('highScore').touched && myForm.get('highScore').hasError('higherThan')">High score should be higher than lower score.</span>
    </div>

  </form>
</div>

TS

export class AppComponent  {
  myForm: FormGroup;

  constructor() {

    this.myForm = new FormGroup({
      highScore: new FormControl(0, [this.lowerThan('lowScore')]),
      lowScore: new FormControl(0, null)
    });
  }
  lowerThan(field_name): ValidatorFn {

    return (control: AbstractControl): { [key: string]: any } => {

      const input = control.value;

      const isLower = control.root.value[field_name] >= input;

      return isLower ? {'lowerThan': {isLower}}: null;
    };
  }
}

找工作StackBlitz Here

【讨论】:

  • 这只会在启动表单时触发,而不是在提交时触发,这不是很有用:D,我错过了什么吗?
猜你喜欢
  • 2018-03-13
  • 1970-01-01
  • 2018-07-20
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
  • 2014-02-09
  • 2021-04-13
  • 1970-01-01
相关资源
最近更新 更多