11

First of all I just begin with Angular 2 and I'm trying to build a nested form and validate it.

Here's part of my ts file:

ngOnInit() {
  this.myForm = this.formBuilder.group({
    projects: this.formBuilder.array([
      this.initProjects()
    ])
  });
}

initProjects(): any {
  return this.formBuilder.group({
    name: ['', [Validators.required, Validators.minLength(3)]],
    some_array: this.formBuilder.array([
      this.formBuilder.group({
        name: ['', Validators.required],
        attr: ['', Validators.required],
        some_id: [1, Validators.required]
      })
    ])
  });
}

addProject(): void {
  const control = < FormArray > this.myForm.controls['projects'];
  control.push(this.initProjects());
}

View:

<form [formGroup]="myForm" novalidate (ngSubmit)="onSubmit(myForm)">
  <div formArrayName="projects">
    <div *ngFor="let project of myForm.controls.projects.controls; let i = index">
      <div [formGroupName]="i">
        <md-input placeholder="Name" formControlName="name"></md-input>
      </div>
      <div *ngFor="let some_obj of project.controls.some_array.controls; let x = index">
        <div [formGroupName]="x">
          <div>
            <md-input placeholder="Nome" formControlName="controls.some_array.controls.name"></md-input>
            <small *ngIf="!some_obj.controls.name.valid">
                    Nome é requerido
                  </small>
          </div>
          <md-input type="number" placeholder="Cost" formControlName="controls.some_array.controls.attr" required></md-input>
        </div>
      </div>
    </div>
  </div>
  <button type="submit" md-raised-button color="primary" [disabled]="!myForm.valid">Submit</button>
</form>
<pre>form value: <br>{{myForm.value | json}}</pre>

The output of form value:

form value: 
{
  "projects": [
    {
      "name": "",
      "some_array": [
        {
          "name": "",
          "attr": "",
          "some_id": 1
        }
      ]
    },
    {
      "name": "",
      "some_array": [
        {
          "name": "",
          "attr": "",
          "some_id": 1
        }
      ]
    }
  ]
}

Well, as you can see I have some arrays called projects, with 1 array inside each one.

So the problem is that I'm not able to validate each control of some_array array.

Actually I'm getting the following error:

ORIGINAL EXCEPTION: Cannot find control with path: 'projects -> 0 -> controls.some_array.controls.name PS: I already tried to put it in a div, as below:

But I also got an error:

Cannot find control with path: 'projects -> some_array' Thanks in advance. Any help would be appreciated.

0

3 Answers 3

10

Try the following HTML:

<form [formGroup]="myForm" novalidate (ngSubmit)="onSubmit(myForm)">
    <div formArrayName="projects">
        <div [formGroupName]="i" *ngFor="let project of myForm.controls.projects.controls; let i = index">
            <md-input placeholder="Name" formControlName="name"></md-input>
            <div formArrayName="some_array">
                <div [formGroupName]="x" *ngFor="let some_obj of project.controls.some_array.controls; let x = index">
                    <div>
                        <md-input placeholder="Nome" formControlName="name"></md-input>
                        <small *ngIf="!some_obj.controls.name.valid">Nome é requerido</small>
                    </div>
                    <md-input type="number" placeholder="Cost" formControlName="attr" required></md-input>
                </div>
            </div>
        </div>
    </div>
    <button type="submit" md-raised-button color="primary" [disabled]="!myForm.valid">Submit</button>
</form>
<pre>form value: <br>{{myForm.value | json}}</pre>
Sign up to request clarification or add additional context in comments.

1 Comment

If there is no array but nested fromGroup so how to show the error message.
0

I experienced the same issue when is used brackets around nested formControlName. For example (this is wrong):

<div formArrayName="options">
            <ion-row *ngFor="let option of addProductForm.controls.options.controls; index as i;">
              <ion-col no-padding [formGroupName]="i">
                <ion-item>
                  <ion-label floating>Name</ion-label>
                  <ion-input type="text" [formControlName]="option_name"></ion-input>
                </ion-item>
              </ion-col>
            </ion-row>
          </div>

formControlName="option_name" must be WITHOUT brackets

Comments

-1

With nested Array. Here is a code sample wich I have tested and runs perfectly in angular6:

app.component.ts:

<pre>
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormArray, FormBuilder, Validators, FormControl, NgControl  } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {

  proxyMedia: FormArray;
  formGroup: FormGroup;

  constructor(
    public formBuilder: FormBuilder
  ) {}

  ngOnInit() {
    this.formGroup = this.formBuilder.group({
      test_name: ['', [Validators.required]],
      tests: this.formBuilder.array([
        this.initTestsForm()
      ])
    });
  }

  initTestsForm(): any {
    return this.formBuilder.group({
      test_num: '',
      categorie: '',
      responses: this.formBuilder.array([
        this.initElement('responses')
      ])
    });
  }

  initElement(elementName: string): FormGroup {
    if(elementName === 'proxy_media') {
      return this.formBuilder.group(
        {
          prefixe: 'prefixe',
          confid: 'confid'
        }
      );
    } else if(elementName === 'tests') {
      return this.formBuilder.group({
        test_num: ['test_num', [Validators.required, Validators.minLength(2)]],
        categorie: ['categorie', [Validators.required, Validators.minLength(2)]],
        responses: this.formBuilder.array([
          this.initElement('responses')
        ])
      });
    } else if(elementName === 'responses') {
      return this.formBuilder.group({
        code_response: ['code_response', Validators.required],
        log_level: ['log_level', Validators.required]
      });
    }
  }

  addElement(formGroup: FormGroup, elementName: string): void {
    const control = < FormArray > formGroup.controls[elementName];
    control.push(this.initElement(elementName));
  }

  removeElement(formGroup: FormGroup, elementName: string, index: number): void {
    const control = <FormArray>formGroup.controls[elementName];
    control.removeAt(index);
  }

  onSubmit(o: any) {
    console.log(o);
  }

  debug(data: any) {
    console.warn('debug: data ');
    console.warn(data);
    console.warn('stop');
  }

}
</pre>

<b>app.component.html:</b>

<h1> Avaibility tests</h1>

<form [formGroup]="formGroup" novalidate (ngSubmit)="onSubmit(formGroup)">
    <input placeholder="Test name" formControlName="test_name">

    <hr>
    <h3>Tests</h3>
    <div formArrayName="tests">
        <div [formGroupName]="testIndex" *ngFor="let test of formGroup.controls.tests.controls; let testIndex = index">
          <h2> Test number #{{testIndex}}</h2>
          <input placeholder="Test number" formControlName="test_num">
          <input placeholder="Category" formControlName="categorie">

            <h3>Responses</h3>
            <hr>
            <div formArrayName="responses">
                <div [formGroupName]="responseIndex" *ngFor="let response of test.controls.responses.controls; let responseIndex = index">
                    <div>
                      <h4> HTTP Response #{{testIndex}}.{{responseIndex}}</h4>
                      <input placeholder="Code response" formControlName="code_response">
                      <small *ngIf="!response.controls.code_response.valid">code response required</small>
                      <input placeholder="Log level" formControlName="log_level">
                    </div>
                    <button type="button" (click)='removeElement(test,"responses",responseIndex)'>Remove Response</button>
                </div>
            </div>
            <hr>
            <button type="button" (click)="addElement(test,'responses')">Add Response</button>
            <br><br>
            <button type="button" (click)='removeElement(formGroup,"tests",testIndex)'>Remove Test</button>
        </div>
    </div>

    <hr>
    <button type="button" (click)='addElement(formGroup,"tests")'>Add Test</button>

    <hr>
    <button type="submit" md-raised-button color="primary" [disabled]="!formGroup.valid">Submit</button>
</form>

<br><br>
</pre>

<b>Add this code at the bottom to debug:</b>
<pre>form value: <br>{{formGroup.value | json}}</pre>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.