1
votes

The official Angular docs have a dynamic form tutorial containing a story for dynamic forms. A service is used to create the FormGroup object like so:

    toFormGroup(questions: QuestionBase<any>[] ) {
    let group: any = {};

    questions.forEach(question => {
      group[question.key] = question.required ? new FormControl(question.value || '', Validators.required)
                                              : new FormControl(question.value || '');
    });
    return new FormGroup(group);
  }

How can I add more than one validator function to each FormControl object? The following doesn't seem to work:

questions.forEach(question => {
      group[question.key] = question.required ? new FormControl(question.value || '', [Validators.required, Validators.maxLength(12)])
                                              : new FormControl(question.value || '');
    });

I've also tried Validators.compose([Validators.required, Validators.maxLength(12)]) which doesn't work as expected either. The only validator that seems to be applied is the 'required' validator. Here is a plnkr demonstrating the behavior. The code mentioned above is contained in the question-control.service.ts file.

The expected outcome i'm trying to achieve is to have the maxLength validator also applied to the FirstName control.

2
@Vega The code is directly from the angular.io site. Only modifications that were made was adding an array of validators to the second param of the FormControl constructor - Mike Lunn
the validator works in your plunkr... What are you expecting as result? - Vega

2 Answers

3
votes

The validations are actually in place, but currently you just have a generic check if the field is not valid or not:

<div class="errorMessage" *ngIf="!isValid">{{question.label}} is required</div>

That will of course display this message if the field is not valid.

The quick solution is to check which error field has:

<div class="errorMessage" *ngIf="form.controls[question.key].hasError('required')">
   {{question.label}} is required
</div>
<div class="errorMessage" *ngIf="form.controls[question.key].hasError('maxlength')">
   {{question.label}} is too long
</div>

plunker: https://plnkr.co/edit/RQRQiJfQbnOHEPuS0jji?p=preview

But since your form is dynamic, I guess you also want a validation that is as dynamic as possible. For that I suggest you take a look at this sample from the official docs which are using an object formErrors and validationMessages with all validation messages stored, and then using these together with this method whenever there are changes in the form:

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] + ' ';
      }
    }
  }
}
0
votes

What you can do is place the necessary validations for each question in an array, I did it this way:

toFormGroup(questions: QuestionBase<any>[] ) {
    const group: any = {};
    let validaciones = [];
    questions.forEach(question => {
      if (question.required) {
        validaciones.push(Validators.required);
      }
      // Validación para solo números
      if (question.number) {
        validaciones.push(Validators.pattern(/^([0-9])*$/));
      }

      group[question.key] = new FormControl(question.value || '', validaciones);
      validaciones = [];

    });

I leave my repository on GitHub if you want to take a look https://github.com/aleRozo/formulariosDinamicos/