Angular 4:如何对后端数据使用自定义验证器

时间:2018-07-19 12:48:31

标签: html angular typescript

在表单中,仅当表单有效时,按钮提交才变为启用状态。特定的输入包含带有后端数据的数据列表。如果用户填写的数据不在数据列表中,我想返回无效的表格。所以,我需要一个自定义的验证器,它检查用户数据是否等于来自后端的数据。 来自后端的数据是对象 listOfArticles 的列表,其中包含参考编号和其他数据。

因此,我尝试在同一组件文件中为自定义验证器创建一个函数,但是由于两个原因,它不起作用:

  • 我不知道如何检索我的 listOfArticles 的路径“ refer.refNumber.input

  • 我在控制台中遇到错误:“无法读取未定义的属性'length'”。

自定义验证程序的功能:

export function ValidateRefNumber(control: AbstractControl) {
  for (let refer of ArbologistiqueComponent.listOfArticles) {
     if (control.value == refer.refNumber.input) {
        return true;
     }
  }
  return null;
}

我的整个 component.ts

import { Component, OnInit } from '@angular/core';
import 'rxjs/add/operator/switchMap';
import { ManagementArbologistiqueService } from "../management-arbologistique.service";
import { ActivatedRoute, Params } from '@angular/router';
import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '@angular/forms';

@Component({
  selector: 'app-arbologistique',
  templateUrl: './arbologistique.component.html',
  styleUrls: ['./arbologistique.component.css']
})

export class ArbologistiqueComponent implements OnInit {

  private reponseTest: String;
  private listOfArticles :Array<Object>
  private pathDownload: any;
  private myFormGroup: FormGroup;
  fileToUpload: File = null;
  private buttonSubmitEnabled: boolean = false;

  constructor(public fb: FormBuilder, private managementArbo: ManagementArbologistiqueService, private route: ActivatedRoute) { }

  ngOnInit() {
    this.myFormGroup = this.fb.group({
      itemRows: this.fb.array([this.initItemRows()])
    })

    this.myFormGroup.valueChanges.subscribe(x => this.buttonSubmitEnabled = false);
    this.getListBdd();
      }


  initItemRows() {
    return this.fb.group({
      ... //other fields
      refNb: ['',[Validators.required, ValidateRefNumber]],
      ... //other fields

    })
  }

  addRow(index: number) {
    console.log("functionAddRow called");
    const control = <FormArray>this.myFormGroup.controls['itemRows'];
    control.insert(index, this.initItemRows());

  }

  deleteRow(index: number) {
    console.log("functionDeleteRow called");
    const control = <FormArray>this.myFormGroup.controls['itemRows'];
    control.removeAt(index);
  }

  sendForm() {
    this.buttonSubmitEnabled=true;
    console.log("functionExportCalled");
    this.route.params.subscribe((params: Params) => {
      let subroute = "exportation";
      this.managementArbo.postProducts(subroute, JSON.stringify(this.myFormGroup.value))
        .subscribe(
          res => { this.reponseTest = res; console.log('reponse:' + res); }

          ,
          err => console.log(err),
          () => console.log('getProducts done'));

    });
  }

  getListBdd() {
    this.route.params.subscribe((params: Params) => {
      let subroute = "getRefNumber";
      this.managementArbo.getProducts(subroute)
        .subscribe(
          res => { this.listOfArticles = res; console.log('reponse:' + res); }

          ,
          err => console.log(err),
          () => console.log('getProducts done'));
    });
  }

  get refNb() {
    return this.myFormGroup.get('itemRows.refNb');
} 
}

export function ValidateRefNumber(control: AbstractControl) {
      for (let refer of ArbologistiqueComponent.listOfArticles) {
         if (control.value == refer.refNumber.input) {
            return true;
         }
      }
      return null;
   }

输入数据列表( component.html ):

<input list="refNumbers" formControlName="refNb" type="text" name="article" maxlength="8" size="15" required title="8 characters" />

<datalist id="refNumbers">
     <option *ngFor="let ref of listOfArticles">{{ref.refNumber.input}}</option>
</datalist>

1 个答案:

答案 0 :(得分:1)

我将为您提供一个简单示例的答案,然后您可以根据需要对其进行转换。
可以说我们有以下文章列表:

  articles = [
    {
      id: 1,
      content: "test 123"
    },
    {
      id: 2,
      content: "test 345"
    }
  ];

我们要检查用户是否在输入文本中输入文章ID之一,否则表单无效。

我们有这小段HTML:

<div *ngFor="let article of articles">
    {{article | json}}
</div>
<form [formGroup]="cForm" (submit)="submitForm(cForm.value)" novalidate>
    <input formControlName="article" type="text" name="article" required />
    <input type="submit" value="submit" [disabled]="!cForm.valid">
</form>

因此,我只打印数组进行调试,并且我们输入了具有formFormName的文章。并且我们有一个按钮,如果表单无效,该按钮将被禁用。

初始化formGroup:

this.cForm = this._fb.group({
  article: [
    null,
    Validators.compose([Validators.required, matchValues(this.articles)])
  ]
});

因此,我们有一个formGroup,其中包含对必填项的简单验证,还有一个名为matchValues的新验证器。

export const matchValues = (valuesToCheck: any[]): ValidatorFn => {
  return (control: AbstractControl): { [key: string]: boolean } => {
    const controlValue = control.value;

    let res = valuesToCheck.findIndex(el => el.id === +controlValue);
    console.log(res);
    return res !== -1 ? null : { matched: true };
  };
};

我只是将其包装在一个接收文章数组的函数中,并且我试图找到与输入文本的值匹配的文章ID。如果找不到任何内容,则返回一个对象:

{ matched: true }

这是控件的错误,因此您可以“匹配”来访问此错误,与访问必需或最小长度或任何其他错误相同。

我希望它足够清楚,可以为您解决一些更复杂的问题提供一个良好的开端。

-更新-
为了更清楚一点:

findIndex在数组中搜索与条件匹配的第一个项目,并返回该项目的索引;如果找不到任何内容,则返回-1;在这种情况下,我已经检查了用户输入的值是否与列表中的任何ID匹配。只要我的res不等于-1,那意味着我找到了一个匹配的项目,因此验证通过,但是如果res === -1则出现错误,所以我发送了错误名称匹配的新对象所以您以后可以处理。无论如何,在这种情况下,我找不到与ID匹配的任何项目,因此它是错误的。

-UPDATE2-
您遇到了一些麻烦,因此我提供了一个有效的示例。 Check this out