【问题标题】:Form Validation (Angular)表单验证(角度)
【发布时间】:2019-01-24 09:44:11
【问题描述】:

我正在使用角度反应形式并制作距离输入字段,其中有两个输入框,分别称为 FromTo

HTML:

<form [formGroup]="form">
  <button (click)="addRow()">Add</button>
  <div formArrayName="distance">
    <div
      *ngFor="let item of form.get('distance').controls; let i = index"
      [formGroupName]="i"
      style="display: flex"
    >
      <input type="number" placeholder="From" formControlName="from" />
      <div><input type="number" placeholder="To" formControlName="to" /></div>
    </div>
  </div>
  <br /><br />
  <button type="submit" [disabled]="!form.valid">Submit</button>
</form>

TypeScript:

ngOnInit() {
  this.form = this.fb.group({
    distance: this.fb.array([]),
  });
  this.addRow()
}

addRow() {
  const control = this.form.controls.distance as FormArray;
  control.push(this.fb.group({
    from: ['',Validators.required],
    to: ['',Validators.required]
  }));
}

这里你可以看到两个输入框默认为fromto

顶部有一个添加按钮,点击添加按钮后,具有相同输入字段的行将被添加并形成数组。

这里我需要限制用户不应该允许输入上一行值,也不能小于该值。

例如,

在第一行,如果用户分别为 from 和 to 输入以下值,例如 05

  "distance": [
    {
      "from": 0,
      "to": 5
    }
  ]

点击添加后,在From输入框的第二行,用户需要被限制添加5和小于5的值(这意味着这些值已经输入了)。

所以这样是无效的,

{
  "distance": [
    {
      "from": 0,
      "to": 5
    },
    {
      "from": 5,
      "to": 10
    }
  ]
}

这里"from": 5,"from": 4(or)3(or)2(or)1,第二行的任何内容都无效..

只有 6 和大于 6 才有效。

同样,对于每一行,它需要检查前一行的值并进行验证。

请帮助我实现这种类型的验证,即限制用户不输入前一行的值(或)小于当前行的值。

工作示例: https://stackblitz.com/edit/disable-group-control-value-on-another-control-value-for-j58atx

编辑:

尝试输入更改,例如,

<input type="number" (input)="onInputChange($event.target.value)" placeholder="From" formControlName="from">

在链接https://stackblitz.com/edit/disable-group-control-value-on-another-control-value-for-ymfpkj 但不确定我是否正确..

如果此程序错误,请更改。

【问题讨论】:

    标签: javascript angular typescript validation angular-reactive-forms


    【解决方案1】:

    最后我决定把这两个条件分开。见new stackblitz

      ngOnInit() {
        this.form = this.fb.group({
          distance: this.fb.array([], this.distanceValidator()),
        });
        this.addRow()
      }
    
      addRow() {
        const control = this.form.controls.distance as FormArray;
        control.push(this.fb.group({
          from: ['', Validators.required],
          to: ['', Validators.required]
        }, { validator: this.greaterValidator() }));
      }
      setDefault() {
        const control = this.form.controls.distance as FormArray;
        this.default.forEach(data => {
          control.push(this.fb.group({
            from: [data.from, Validators.required],
            to: [data.to, Validators.required]
          }, { validator: this.greaterValidator() }));
        });
      }
      greaterValidator() {
        return (fa: FormGroup) => {
          return fa.value.to && fa.value.to < fa.value.from ? { error: "from greater than to" } : null;
        }
      }
      distanceValidator() {
        return (fa: FormArray) => {
          let ok = true;
          for (let i = 1; i < fa.value.length; i++) {
            ok = (!fa.value[i].from || fa.value[i].from > fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > fa.value[i - 1].from);
            if (!ok)
              return { error: "from/to yet included", index: i }
          }
          return null
        }
      }
    

    还有.html

    <form [formGroup]="form">
        <button (click)="addRow()">Add</button>
      <div formArrayName="distance" >
        <div 
          *ngFor="let item of form.get('distance').controls; let i = index" 
          [formGroupName]="i" 
          style="display: flex">
          <input type="number" 
            placeholder="From" 
            formControlName="from">
          <div>
            <input type="number"
              placeholder="To" 
              formControlName="to">
          </div>
          <span *ngIf="item.errors">*</span>
          <span *ngIf="form.get('distance')?.errors && form.get('distance')?.errors.index==i">**</span>
        </div>
      </div>
      <div *ngIf="form.get('distance')?.errors">{{form.get('distance')?.errors.error}}</div>
      <br><br>
      <button type="submit" [disabled]="!form.valid"> Submit </button>
    </form>
    <button (click)="setDefault()"> Set Default Values </button>
    

    更新:其实只在发现错误的时候才多管。 此外,如果 from 和 to 之前为空,不要报错。为了避免这种情况,我们可以“转换”为数字,写作

    let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to)
            && (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
    

    (见+fa.value[i-1].to和+fa.value[i-1].from中的“+”

    好吧,当我们决定我们发送的错误时,假设您有 6 行,并且位置 0、位置 3 和位置 4(0 是第一行)的行发送类似的错误

    {error:"there are errors",indexError:",0,3,4,"}
    

    这允许在*ngFor里面写一些like

      <span *ngIf="form.get('distance')?.errors && 
          form.get('distance')?.errors.indexError.indexOf(','+i+',')>=0">
           **
      </span>
    

    嗯,我们的 distanceValidator 变成了这样的

      distanceValidator() {
        return (fa: FormArray) => {
          let indexError:string="";
          for (let i = 1; i < fa.value.length; i++) {
            let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
            if (!ok)
              indexError+=','+i;
          }
          return indexError?{error:"there are errors",indexError:indexError+','}:null
        }
    

    有人可能认为最好返回一个错误数组,但这不允许以简单的方式知道有错误的行。一些类似 errors.find(x=>x.id==i) 不起作用,因为我们不能在插值中使用 find。

    确实只将一行与之前的中间值进行比较。在使用 for (let j=i-1;j>0;j++){ok=ok && ...}- 之前可以检查所有内容,但我认为这不是必需的,我们必须在代码中小气。请记住,函数 distanceValidator 会执行多次 见另一个stackblitz

    【讨论】:

    • 欢迎使用您的新解决方案.. 但是您单击设置默认按钮,其中第一行的值为 0 和 5,第二行的值为 6 和 2.. 但我看不到任何错误消息显示..您认为第二行中的 2 是有效输入吗??
    • 见*和**
    • 不着急Eliseo,我知道您的解决方案,因此我仍然期待您提供一个好的解决方案。请给我一个很好的解决方案,解决我需要的问题,并为每个解决方案显示正确的错误消息。会给你这个解决方案的投票。如果你编辑这个并显示正确的错误消息,那么我会接受这个新的解决方案。请理解我需要什么 Eliseo ..
    • Eliseo 另一个与此问题相关的问题,我需要根据行驶距离计算票价stackoverflow.com/questions/54427048/…
    • 请看上面给出的问题Eliseo,我真的需要你的帮助..
    【解决方案2】:

    只需使用customValidation(我选择在同一个组件中进行验证

    ngOnInit() {
        this.form = this.fb.group({
          distance: this.fb.array([], this.distanceValidator()),
        });
        this.addRow()
      }
      distanceValidator() {
        return (fa: FormArray) => {
          let ok = true;
          let ok2 = fa.value.length ? (!fa.value[0].to || !fa.value[0].from) || fa.value[0].to > fa.value[0].from : true;
          if (!ok2)
            return { error: "from greater than to" }
          for (let i = 1; i < fa.value.length; i++) {
            if (fa.value[i].from && fa.value[i].to )
            {
            ok = (fa.value[i].from > fa.value[i - 1].to || fa.value[i].to < fa.value[i - 1].from);
            ok2 = (fa.value[i].to > fa.value[i].from);
            if (!ok)
              return { error: "from/to yet included" }
            if (!ok2)
              return { error: "from greater than to" }
            }
          }
          return ok && ok2 ? null : !ok?{ error: "from yet included" }:{ error: "from greater than to" }
        }
      }
    

    你可以看到另一个错误

     <div *ngIf="form.get('distance')?.errors">
         {{form.get('distance')?.errors.error}}
     </div>
    

    参见 [stackblitz 分叉][1]

    【讨论】:

    • 谢谢亲爱的 Eliseo,错误信息没有显示出来。我们如何按照你的例子显示错误信息??
    • 您可以显示错误“从大于到”创建组的自定义验证器(并更改 distanceValidator 以简化它)
    • Eliseo 还请更新您提供的 stackblitz 并提供错误消息。再次感谢您的时间和帮助。如果我需要帮助,会回复您。
    • 请重新检查 stackblitz,如果我输入了错误的值,我将无法收到错误消息..
    • 在发件箱中没有显示错误信息,请检查它..对于输入框它的显示..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-14
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 2018-08-18
    相关资源
    最近更新 更多