Search code examples
angularjsvalidationrxjsdynamic-forms

how to reactive validation messages in dynamic forms in angular 2


I am learning reactive dynamic forms in angular 2.

I want to validation messages like angular.io cookbook form-validation does.

Something like this:

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

So I subscribe form valuechanges in dynamic forms

but strange thing is happen, something like:

I have two addrees from group, one is valid, two is invalid.

So error message should be one, but the result is both!!

would someone help fix this?

here is run file

Here is adress html and ts file

import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    moduleId: module.id,
    selector: 'address',
    templateUrl: 'address.component.html',
})
export class AddressComponent {
    @Input('group')
    public adressForm: FormGroup;
}
<div [formGroup]="adressForm">
    <div class="form-group col-xs-6">
        <label>street</label>
        <input type="text" class="form-control" formControlName="street">
        <small [hidden]="adressForm.controls.street.valid" class="text-danger">
            Street is required
        </small>
    </div>
    <div class="form-group col-xs-6">
        <label>postcode</label>
        <input type="text" class="form-control" formControlName="postcode">
    </div>
</div>

Here is app

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormArray, FormBuilder, Validators } from '@angular/forms';
import { Customer } from '../model/customer.model';

@Component({
    selector: 'customer-component',
    templateUrl: 'customer.component.html',
})
export class CustomerComponent implements OnInit {
    public customerForm: FormGroup;

    constructor(private _fb: FormBuilder) { }

    ngOnInit() {
        this.customerForm = this._fb.group({
            'name': ['', [Validators.required, Validators.minLength(5)]],
            'addresses': this._fb.array([])
        });

        // add address
        this.addAddress();

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

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

    initAddress() {
        return this._fb.group({
            'street': ['', Validators.required],
            'postcode': ['']
        });
    }

    addAddress() {
        const control = <FormArray>this.customerForm.controls['addresses'];
        const addrCtrl = this.initAddress();

        control.push(addrCtrl);

    }

    removeAddress(i: number) {
        const control = <FormArray>this.customerForm.controls['addresses'];
        control.removeAt(i);
    }

    onSubmit() {

    }

    onValueChanged(data?: any) {

        const form = this.customerForm;

        for (const field in this.formErrors) {
            // clear previous error message (if any)

            const control = form.get(field);

            if (control instanceof FormArray) {

                const fmArray = <FormArray>control;
                console.log("from Array have count : " + fmArray.controls.length);
                this.formErrors[field] = [];

                fmArray.controls.forEach(g => this.formErrors[field].push(this.addressErrors));

                let groupIndex = 0;
                for (const groupItem of fmArray.controls) {
                    const group = <FormGroup>groupItem;

                    console.log("group index is here == " + groupIndex);

                    for (const ctrlField in this.addressErrors) {
                        const gpControl = group.controls[ctrlField];

                        this.formErrors[field][groupIndex][ctrlField] = '';

                        if (gpControl && gpControl.dirty && !gpControl.valid) {
                            const messages = this.validationMessages[ctrlField];
                            for (const key in gpControl.errors) {
                                console.log("control error key is here ==" + key);
                                this.formErrors[field][groupIndex][ctrlField] += messages[key] + ' ';

                                console.log("set value on object of key is here ==" + ctrlField)
                                console.log(this.formErrors[field][groupIndex]);
                            }
                        }

                    }
                    groupIndex++;
                }

            }
            else {
                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] + ' ';
                    }
                }
            }

        }
    }

    formErrors: any = {
        'name': '',
        'addresses': []
    };

    addressErrors: any = {
        'street': ''
    }

    validationMessages: any = {
        'name': {
            'required': 'Name is required.',
            'minlength': 'Name must be at least 4 characters long.'
        },
        'street': {
            'required': 'Street is required.',
        }
    };
}
<h1>
    Customer Dynamic Static Form Reactive</h1>
<form [formGroup]="customerForm" (ngSubmit)="onSubmit()">
    <div class="form-group">
        <label for="name">Name</label>

        <input type="text" id="name" class="form-control" formControlName="name">

        <div *ngIf="formErrors.name" class="alert alert-danger">
            {{ formErrors.name }}
        </div>
    </div>

    <!--addresses-->
    <div formArrayName="addresses">
        <div *ngFor="let address of customerForm.controls.addresses.controls; let i=index" class="panel panel-default">
            <div class="panel-heading">
                <span>Address {{i + 1}}</span>
                <span class="glyphicon glyphicon-remove pull-right" *ngIf="customerForm.controls.addresses.controls.length > 1" (click)="removeAddress(i)"></span>
            </div>
            <div class="panel-body" [formGroupName]="i">
                <address [group]="address"></address>
            </div>
        </div>
    </div>

    <div class="margin-20">
        <a (click)="addAddress()" style="cursor: default"> Add another address +
</a>
</div>

<div class="margin-20">
    <button type="submit" class="btn btn-primary pull-right" [disabled]="!customerForm.valid">Submit</button>
</div>
<div class="clearfix"></div>


<div class="margin-20">
    <pre>formErrors: <br>{{formErrors  | json}}</pre>
    <div>myForm details:-</div>
    <pre>Is myForm valid?: <br>{{customerForm.valid | json}}</pre>
    <pre>form value: <br>{{customerForm.value | json}}</pre>
</div>
</form>

html and ts file


Solution

  • Have you tried adapting the onValueChanged method outlined in the Angular docs?

    onValueChanged(data?: any) {
      if (!this.heroForm) { return; }
      const form = this.heroForm.form;
    
      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] + ' ';
          }
        }
      }
    }
    
    formErrors = {
      'name': '',
      'power': ''
    };