36
votes

I have a data driven form in angular2 like below

this.formBuilder.group({
  'name': ['',Validators.required],
  'description': ['', Validators.required],
  'places': this.formBuilder.array([], Validators.minlength(1)) 
})

I want to add validations to the place formArray, so i am adding minlength validation, but minlength validation is not working on formArray.

What are the other validations for formArray, so that form must be valid only when places array contain atleast one place.

7
Scratch that is should work github.com/angular/angular/commit/… - Aluan Haddad
You may also be interested in this github.com/angular/angular/commit/… anyway this should work make sure you have an up-to-date version - Aluan Haddad

7 Answers

48
votes

Validators.required does the magic for you:

// using required 

this.formGroup = this.formBuilder.group({
    taskTreeId: [Common.UID()],
    executionProgrammedTime: ["", [Validators.required]],
    description: [""],
    tasks: this.formBuilder.array([], Validators.required)
});  

// using custom validation

export const minLengthArray = (min: number) => {
  return (c: AbstractControl): {[key: string]: any} => {
    if (c.value.length >= min)
      return null;

    return { MinLengthArray: true};
  }
}

 this.formGroup = this.formBuilder.group({
        taskTreeId: [Common.UID()],
        executionProgrammedTime: ["", [Validators.required]],
        description: [""],
        tasks: this.formBuilder.array([], minLengthArray(2))
    });
<button type="button" class="btn btn-success btn-rounded" 
    [disabled]="!formGroup.valid">SAVE</button>
31
votes

Add this custom validator to your validation service:

minLengthArray(min: number) {
    return (c: AbstractControl): {[key: string]: any} => {
        if (c.value.length >= min)
            return null;

        return { 'minLengthArray': {valid: false }};
    }
}

And then when creating the form do the following:

this.formBuilder.group({
  'name': ['',Validators.required],
  'description': ['', Validators.required],
  'places': this.formBuilder.array([], this.validationService.minLengthArray(1)) 
});

And you can check errors against the FormArray by checking FormArray.hasError('minLengthArray')

8
votes

because you using wrong validator function name: minlength -> minLength

demo code:

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

@Component({
  selector: 'app-root',
  template: `
    <form novalidate [formGroup]="tpForm" (ngSubmit)="onSubmit()">
      <div><input type="text" formControlName="name"></div>
      <div><textarea formControlName="description"></textarea></div>
      <div formArrayName="places">
        <button type="button" (click)="addPlace()">+</button>
        <div *ngFor="let place of places.controls; let i = index">
          <div>
              <span>Places {{i + 1}}</span>
              <button type="button" *ngIf="places.controls.length > 0" 
                  (click)="removePlace(i)">
                  x
              </button>
          </div>
          <input type="text" [formControlName]="i">
        </div>
      </div>
      <button type="submit">Submit</button>
    </form>

    <p>Status: {{ tpForm.valid }}</p>
  `,
  styles: [
    `


    `
  ]
})
export class AppComponent implements OnInit {
  tpForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.tpForm = this.fb.group({
      'name': ['',Validators.required],
      'description': ['', Validators.required],
      'places': this.fb.array([
        this.fb.control('')
      ], Validators.minLength(1))
    })
  }

  get places(): FormArray {
    return this.tpForm.get('places') as FormArray;
  }

  onSubmit() {

  }

  addPlace() {
    this.places.push(this.fb.control(''));
  }

  removePlace(index) {
    this.places.removeAt(index);
  }

}

Plunker: https://plnkr.co/edit/cfi7nN?p=preview

7
votes

Good and clear way to do this is :

Create a Array.validator.ts and put all the custom validations over there , like minLength , maxLength etc

import { ValidatorFn, AbstractControl, FormArray } from '@angular/forms';

// Array Validators
export class ArrayValidators {

    // max length
    public static maxLength(max: number): ValidatorFn | any {
        return (control: AbstractControl[]) => {
            if (!(control instanceof FormArray)) return;
            return control.length > max ? { maxLength: true } : null;
        }
    }

    // min length
    public static minLength(min: number): ValidatorFn | any {
        return (control: AbstractControl[]) => {
            if (!(control instanceof FormArray)) return;
            return control.length < min ? { minLength: true } : null;
        }
    }
}

And Then just import that file and use validation wherever you want to :

import { ArrayValidators } from './array.validator'; // Import it

'hobbies':new FormArray([],ArrayValidators.minLength(2)) // Use it

WORKING DEMO


Note :

You can directly use this package also ng-validator ( Reference is taken from this package ) , But If you need just required array validations you can do it like above.

5
votes

if you are trying to add validation to formarray try this may help you,

this.formBuilder.group({
  'name': ['',Validators.required],
  'description': ['', Validators.required],
  'places': this.formBuilder.array(this.initPlaces()) 
})

initPlaces() {       
        return this._fb.group({
            places: ['',[Validators.minLength(1)]]           
        });
  }

this initPlaces will simply return formGroup with validation as per requirement.

1
votes

Having a template

<div formArrayName="items">
    <div *ngFor="[...] let i = index" [formGroupName]="i">

    [...]

    </div>
</div>

You could simply use this in your button :

[disabled]="!myForm.get('items.1')"
0
votes
this.profileForm = this.fb.group({
      firstName: [''],
      lastName: [''],
      hobbies: this.fb.array([this.fb.control('')], Validators.minLength(2))
});

Just use the Validators.minLength(length). https://angular.io/api/forms/Validators#minLength