13

我的目标是将所有验证消息放在组件而不是 html 文件中

我有一个注册页面,下面是字段:

public buildRegisterForm() {
this.userForm = this.fb.group({
  firstName: ['', [Validators.required, Validators.minLength(3)]],
  lastName: ['', [Validators.required, Validators.maxLength(50)]],
  emailGroup: this.fb.group({
    email: ['', [Validators.required, Validators.pattern(this.emailPattern)]],
    retypeEmail: ['', Validators.required],
  }, { validator: formMatcherValidator('email', 'retypeEmail') }),
  passwordGroup: this.fb.group({
    password: ['', [Validators.required, strongPasswordValidator()]],
    retypePassword: ['', Validators.required],
  }, { validator: formMatcherValidator('password', 'retypePassword')}),
});
}

我正在按照这个教程链接来实现我想要的,即将我所有的验证消息放在组件文件而不是 html 文件中。

export const validationMessages = {
'firstName': {
'required': 'Your first name is required.',
'minlength': 'Your first name must be at least 3 characters long.'
},
'lastName': {
'required': 'Your last name is required.',
'minlength': 'Your last name must be less than 50 characters long.'
},
'emailGroup': {
  'email': {
      'required': 'Your email is required',
      'pattern': 'Your login email does not seem to be a valid email address.'
     },
 'retypeEmail': {
      'required': 'Your retype email is required',
      'match': 'The email provided do not match.'
   },
},
'passwordGroup':{
     'password': {
         'required': 'Your password is required',
         'strongPassword': 'Your password must be between 8 - 15 characters and must contain at least three of the following: upper case letter, lower case letter, number, symbol.'
     },
   'retypePassword': {
       'required': 'Your retype password is required',
        'match': 'The password provided do not match.'
  }
}


onValueChanged 方法

private onValueChanged(data?: any) {
if (!this.userForm) { return; }
const form = this.userForm;

// tslint:disable-next-line:forin
for (const field in this.formErrors) {
  // clear previous error message (if any)
  this.formErrors[field] = '';
  let control = form.get(field);
  // console.log("control", control.dirty);

  console.log("controlEmail", control);
  if (control && (control.dirty || control.touched) && control.invalid) {
    let messages = validationMessages[field];
    // tslint:disable-next-line:forin
    for (const key in control.errors) {
      this.formErrors[field] += messages[key] + ' ';
    }
  }
 }
}

当我有多个 formBuider Group 或嵌套对象时,此方法不起作用。这个1有什么建议吗?
类似于如何使用嵌套表单组验证反应式表单?

4

4 回答 4

8

在我看来,您需要在 - 方法中创建一个嵌套循环onValueChanged(data)。由于您有很多嵌套组,因此我不会复制它。但是嵌套循环是通用的,因此它适用于您的所有组。但这里是一个只有一个嵌套组而不是多个嵌套组的示例。我正在使用英雄示例。

嵌套组名是group,里面的formcontrol被调用child

formErrors因此,在代码中使用的应该child在里面group

formErrors = {
  'name': '',
  'power': '',
  'group':{
    'child': ''
  }
};

因此您必须记住,当您在模板中添加验证时,您需要使用:

<div *ngIf="formErrors.group.child">
   {{ formErrors.group.child }}
</div>

验证消息不会在里面group,但就像其他验证消息一样:

validationMessages = {
  'name': {
    'required': 'Name is required.',
  },
  'power': {
    'required': 'Power is required.'
  },
  'child': {
    'required': 'Child is required.',
  }
};

最后,修改onValueChanges

onValueChanged(data?: any) {
  if (!this.heroForm) { return; }
  const form = this.heroForm;

  // iterate toplevel of formErrors
  for (const field in this.formErrors) {
    // check if the field corresponds a formgroup (controls is present)
    if(form.get(field).controls ) {
      // if yes, iterate the inner formfields
      for(const subfield in form.get(field).controls) {
        // in this example corresponds = "child", reset the error messages
        this.formErrors[field][subfield] = '';
        // now access the actual formfield
        const control = form.get(field).controls[subfield];
        // validate and show appropriate error message
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[subfield];
          for (const key in control.errors) {
            this.formErrors[field][subfield] += messages[key] + ' ';
          }
        }
      }
    } 
    // does not contain a nested formgroup, so just iterate like before making changes to this method
    else {
      const control = form.get(field);
      this.formErrors[field] = '';
      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      } 
    }
  }
}

最后,一个DEMO :)

普朗克

您必须记住,尽管在您的情况下这是可行的,但是如果嵌套组内有嵌套组,这将不起作用,那么您必须在 中再创建一个循环onValueChanges,但您没有这里的问题;)

于 2017-04-13T15:36:02.573 回答
3

您还可以在原始onValueChanged方法旁边使用以下内容:

formErrors = {
  'name': '',
  'power': '',
  'group.child':''
};

validationMessages = {
  'name': {
    'required': 'Name is required.',
  },
  'power': {
    'required': 'Power is required.'
  },
  'group.child': {
    'required': 'Child is required.',
  }
};

onValueChanged(data?: any) {
    if (!this.heroForm) { return; }
    const form = this.heroForm;

    for (const field in this.formErrors) {
      // clear previous error message (if any)
      this.formErrors[field] = '';
      const control = form.get(field);

      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      }
    }
  }
于 2017-07-08T21:26:11.927 回答
1

我希望 Nehal 的解决方案有效,但我无法做到。在使用@AJT_82 代码后想出了我的解决方案。我的需求真的来自想要作为一个组检查密码,而他的解决方案没有涵盖这一点。所以,我已经包括了我用来创建对我有用的完整解决方案的其他部分。

在发现我在 2 中使用的方法不再有效之后,我尝试在 Angular 4 中验证密码确认时遇到了这个问题。angular.io 上的信息对此并没有太大帮助,因为许多信息分散在文档的不同区域。

因此,澄清一下,这遵循 Angular 的响应式表单方法。我写这个是为了在我还有其他验证限制的情况下验证密码(密码必须在 4 到 24 个字符之间,是必需的,等等)。通过一些小的调整,这种方法应该同样适用于电子邮件确认。

首先,为了比较组的验证器,html 表单必须有一个使用formGroupName=" "标识符标识的子组。这是在主[formGroup]标识符内。比较的输入必须在这个formGroupName标记的元素内。就我而言,它只是一个 div。

<div class="title">Hero Registration Form</div>
<form [formGroup]="regForm" id="regForm" (ngSubmit)="onSubmit()">
    <div class="input">
        <label for="heroname">Heroname</label> <input type="text"
            id="heroname" class="form-control" formControlName="heroname"
            required />
        <div *ngIf="formErrors.heroname" class="hero-reg-alert">{{
            formErrors.heroname }}</div>
    </div>
    <div class="input">
        <label for="email">Email</label> <input type="email" id="email"
            class="form-control" formControlName="email" required />
        <div *ngIf="formErrors.email" class="hero-reg-alert">{{
            formErrors.email }}</div>
    </div>
    <div formGroupName="password">
        <div class="input">
            <label for="password1">Password</label> <input type="password"
                id="password1" class="form-control" formControlName="password1"
                required />
            <div *ngIf="formErrors.password.password1" class="hero-reg-alert">
                {{ formErrors.password.password1 }}</div>
        </div>
        <div class="input">
            <label for="password2">Re-Enter Password</label> <input
                type="password" id="password2" class="form-control"
                formControlName="password2" required />
            <div
                *ngIf="formErrors.password.password2 || formErrors.password.password"
                class="hero-reg-alert">{{ formErrors.password.password }} {{
                formErrors.password.password2 }}</div>
        </div>
    </div>
    <button type="submit" [disabled]="!regForm.valid">
        <span id="right-btntxt">SUBMIT</span>
    </button>
</form>

您可能会注意到我有 formErrors 的子值

formErrors.password.password
formErrors.password.password1
formErrors.password.password2

这些是内置在我的代码中的......

  formErrors = {
    'username': '',
    'email': '',
    'password': {
      'password': '',
      'password1': '',
      'password2': ''
    }
  };

我以这种方式构建它,因为“password1”和“password2”为每个字段保存我的 formErrors,而“password”在不匹配的情况下保存我的错误(其中两个输入的密码不相等)。

这是可能的 onValueChanged() 公式。它检查一个字段是否是 FormGroup 的一个实例。如果是这样,它首先检查该字段组的自定义验证(将它们存储在 'this.formErrors[field][field]' 中),然后继续处理子字段验证。如果它不是一个 instanceof FieldGroup,则根据 angular.io 指导文档中的示例处理验证。

  onValueChanged(data?: any) {
    if (!this.regForm) {return;}
    const form = this.regForm;

    for (const field in this.formErrors) {
      const formControl = form.get(field);
      if (formControl instanceof FormGroup) {
        this.formErrors[field][field] = '';
        // check for custom validation on field group
        const control = form.get(field);
        // handle validation for field group
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[field];
          for (const key in control.errors) {
            this.formErrors[field][field] += messages[key] + ' ';
          }
        }
        // handle validation for subfields
        for (const subfield in formControl.controls) {
          console.log('SUBFIELD', subfield);
          this.formErrors[field][subfield] = '';
          const control = formControl.controls[subfield];
          if (control && control.dirty && !control.valid) {
            const messages = this.validationMessages[subfield];
            for (const key in control.errors) {
              this.formErrors[field][subfield] += messages[key] + ' ';
            }
          }
        }
      } 
        // alternate validation handling for fields without subfields (AKA not in a group)
      else {
        const control = form.get(field);
        this.formErrors[field] = '';
        if (control && control.dirty && !control.valid) {
          const messages = this.validationMessages[field];
          for (const key in control.errors) {
            this.formErrors[field] += messages[key] + ' ';
          }
        }
      }
    }
  }

通过为 FieldGroup 提供一个具有自己名称的子字段,我们可以将验证存储在 FieldGroup 上。尝试使用常规 onValueChange 代码执行此操作会覆盖该行的子字段...

    this.formErrors[field] = '';

不提供存储 FieldGroup 验证的位置会覆盖子字段或不处理 FieldGroup。

如果您需要它,这就是使用 buildFomr() 构建表单的方式;

 buildForm(): void {
    this.regForm = this.formBuilder.group({
      'username': [this.registerUser.username, [
        Validators.required,
        Validators.minLength(4),
        Validators.maxLength(24)
      ]
      ],
      'email': [this.registerUser.email, [
        Validators.email,
        Validators.required
      ]
      ],
      'password': this.formBuilder.group({
        'password1': [this.registerUser.password, [
          Validators.required,
          Validators.minLength(8),
          Validators.maxLength(24)
        ]
        ],
        'password2': ['', [
          Validators.required
        ]
        ]
      }, {validator: this.passwordMatchValidator})
    }); // end buildForm

    this.regForm.valueChanges
      .subscribe(data => this.onValueChanged(data));

    this.onValueChanged(); // (re)set validation messages now
  }

这是自定义验证功能...

  passwordMatchValidator(control: FormGroup): {[key: string]: any} {
    return control.get('password1').value !== control.get('password2').value ? {mismatch: true} : null;
  }
于 2017-07-18T17:31:09.547 回答
0
 public morningForm:FormGroup;

 constructor(public fb: FormBuilder) { }
 ngOnInit() {
 this.morningForm = this.fb.group({
      'country': [''],
      'city' : [''],
      'phone': ['']
    });
    this.setValidators();
}
private setValidators() {
    let me = this;
    let status = false;
    me.morningForm.valueChanges.subscribe(fieldObj => {
      for (const fieldName in fieldObj) {
      // IF ENTER OR FIND THE VALUE THEN ALL FIELD SET AUTO REQUIRED VALIDATION.
        if (fieldObj[fieldName] != '') {
          status = true;
          break;
        }
      }
    });
    this.setRequiredField(status)
  }

  private setRequiredField(status:any) {
    let me = this;
    let country = me.morningForm.get('country');
    let city = me.morningForm.get('city');
    let phone = me.morningForm.get('phone');

    country.setValidators(null);
    city.setValidators(null);
    phone.setValidators(null);
    if (status) {
      country.setValidators([Validators.required]);
      city.setValidators([Validators.required]);
      phone.setValidators([Validators.required]);
    }
    country.updateValueAndValidity();
    city.updateValueAndValidity();
    phone.updateValueAndValidity();
  }
于 2020-02-28T06:11:47.180 回答