12

I have been trying to use FormArray for a div that is dynamically added by the user, but I can't find the controls for the user inputs! I always have the error:

Error: Cannot find control with path: 'textoAvisos -> 0 -> assTipo'

Error: Cannot find control with path: 'textoAvisos -> 0 -> msgTipo'

Error: Cannot find control with path: 'textoAvisos -> 0 -> dataTipo'

The div contains 3 inputs that the user need to insert, which it seems that the control cannot find them. A new div is added after the user click a button, hence why it needs to be dynamic, but that is not the issue. I don't need to worry about the push insert for the moment, as I need to validate the input from the user first!

Here is the HTML:

<form style="text-align: center;" [formGroup]="janelaAtualizacaoForm" (ngSubmit)="cadastrarJanelaAtualizacao()">
    <div *ngFor="let disparaEmail of disparaEmails; let i=index" formArrayName="textoAvisos" class="ui-g-4" style="margin-right: 10px; border: 1px solid #c8c8c8; border-radius: 5px; min-width: 466.828px;">
                    
        <div [formGroupName]="i">
            <p class="titulo-campo font1 fw700">Assunto:</p>
            <textarea pInputTextarea [rows]="2" formControlName="assTipo" required style="width: 100%; resize:unset; font-size: 18px;"></textarea>

            <p class="titulo-campo font1 fw700">Tipo de Aviso:</p>
            <p-editor [style]="{'height':'300px'}" formControlName="msgTipo" required></p-editor>

            <p class="titulo-campo font1 fw700">Data:</p>
            <p-calendar [readonlyInput]="true" formControlName="dataTipo" dateFormat="dd/mm/yy" showButtonBar="true" [locale]="localeService.getLocale()"[monthNavigator]="true" [yearNavigator]="true" yearRange="2018:2050" required></p-calendar>
        </div>
                    
    </div>
</form>

And here is the TS:

constructor(
    private janelaAtualizacaoService: JanelaAtualizacaoService,
    private segmentoInfoService: SegmentoInformacaoService,
    private empresaService: EmpresaService,
    private route: ActivatedRoute,
    private router: Router, private fb: FormBuilder, private location: Location,
    private changeDetector: ChangeDetectorRef, private localeService: LocaleService
) {
    this.criarJanelas();
}

criarJanelas() {
    this.janelaAtualizacaoSelecionado = [];
    this.janelaAtualizacaoForm = new FormGroup({
        textoAvisos: new FormArray([
            new FormControl(
                new FormGroup({
                    assTipo: new FormControl('', [Validators.required]),
                    msgTipo: new FormControl('', [Validators.required]),
                    dataTipo: new FormControl('', [Validators.required])
                })
            )
        ])
    });
}

Thanks for the help, everyone!

Community
  • 1
  • 1
Leminur
  • 291
  • 2
  • 7
  • 22

1 Answers1

15

You're using [formGroupName] incorrectly. In your line with <div [formGroupName]="i">, you are trying to get the formGroupName via the index i, which won't work because you have not created any FormGroups that have a number as a name.

I believe the Angular tutorial on reactive forms will help you, specifically the part about FormArrays and dynamic controls: https://angular.io/guide/reactive-forms#dynamic-controls-using-form-arrays

To fix your problem, you probably need do the following changes.

HTML:

Change <div [formGroupName]="i"> to <div [formGroup]="textoAvisos.controls[i]">

or

change *ngFor="let disparaEmail of disparaEmails; let i=index" to *ngFor="let formGroup of textoAvisos.controls; let i=index"

The first example change is provided below.

    <form style="text-align: center;" [formGroup]="janelaAtualizacaoForm" (ngSubmit)="cadastrarJanelaAtualizacao()">
    <div *ngFor="let disparaEmail of disparaEmails; let i=index" formArrayName="textoAvisos" class="ui-g-4" style="margin-right: 10px; border: 1px solid #c8c8c8; border-radius: 5px; min-width: 466.828px;">

        <div [formGroup]="textoAvisos.controls[i]">
            <p class="titulo-campo font1 fw700">Assunto:</p>
            <textarea pInputTextarea [rows]="2" formControlName="assTipo" required style="width: 100%; resize:unset; font-size: 18px;"></textarea>

            <p class="titulo-campo font1 fw700">Tipo de Aviso:</p>
            <p-editor [style]="{'height':'300px'}" formControlName="msgTipo" required></p-editor>

            <p class="titulo-campo font1 fw700">Data:</p>
            <p-calendar [readonlyInput]="true" formControlName="dataTipo" dateFormat="dd/mm/yy" showButtonBar="true" [locale]="localeService.getLocale()"[monthNavigator]="true" [yearNavigator]="true" yearRange="2018:2050" required></p-calendar>
        </div>

    </div>
</form>

Typescript:

Remove the surrounding FormControl from your FormGroup in textoAvisos and add a getter for textoAvisos. Without this getter, you will get an error regarding textoAvisos being undefined. What tripped us up was that we were using textoAvisos in formArrayName="textoAvisos, but you are able to use textoAvisos like that because formArrayName explicitly looks for a formArray on the janelaAtualizacaoForm. When we try to do textoAvisos.controls in the *ngFor we get an error because we don't actually have a property in our component class to bind too with that name, since textoAvisos exists only as an element on the janelaAtualizacaoForm form.

constructor(
    private janelaAtualizacaoService: JanelaAtualizacaoService,
    private segmentoInfoService: SegmentoInformacaoService,
    private empresaService: EmpresaService,
    private route: ActivatedRoute,
    private router: Router, private fb: FormBuilder, private location: Location,
    private changeDetector: ChangeDetectorRef, private localeService: LocaleService
) {
    this.criarJanelas();
}

public get textoAvisos() {
    return this.janelaAtualizacaoForm .get('textoAvisos') as FormArray;
}

criarJanelas() {
    this.janelaAtualizacaoSelecionado = [];
    this.janelaAtualizacaoForm = new FormGroup({
        textoAvisos: new FormArray([
            new FormGroup({
                assTipo: new FormControl('', [Validators.required]),
                msgTipo: new FormControl('', [Validators.required]),
                dataTipo: new FormControl('', [Validators.required])
            })
        ])
    });
}

I have not tested these in a live environment but hopefully they will solve your problem.

Willwsharp
  • 702
  • 1
  • 7
  • 25
  • 1
    I have read dozens of times and followed the guide you showed, I used only one input field as testing (using "i" as the formControlName). I did not receive an error message like the one from above when I first run the page, but after I added a new div, I received the following error: `Cannot find control with path: 'textoAvisos -> 1'`. I am not sure why I received that. – Leminur Oct 29 '18 at 20:42
  • 1
    The way you have your code now, you are trying to retrieve a FormGroup on textoAvisos that has the name `0`, `1`, `2` and so on. That won't work as there are no FormGroups with those names on your FormArray. I will update my answer with what I think you need to do in just a bit. – Willwsharp Oct 29 '18 at 20:52
  • Ohhh! I see what you mean now, by using array on controls, you can access each different div. I think I may have not added something though, as I am getting this error on both methods you explained above: `Error: Cannot read property 'controls' of undefined`. – Leminur Oct 29 '18 at 21:16
  • You may have to add another div outside of your `*ngFor` that contains the formArrayName directive, like so:
    ... *ngFor and other html
    – Willwsharp Oct 29 '18 at 21:24
  • Hmm, I have done this: `
    ` The error is still happening!
    – Leminur Oct 29 '18 at 21:29
  • 1
    You will need to play around with it and figure out why `textoAvisos` is undefined. I will try to help later. – Willwsharp Oct 29 '18 at 21:31
  • I will do! Thanks for the help and enlightenment anyway! You are the life-saver man! – Leminur Oct 29 '18 at 21:34
  • No problem. I think I figured it out; will update my answer. – Willwsharp Oct 29 '18 at 22:08
  • Sorry for the late reply @Willwsharp! I have implemented the changes, I have edited your answer to insert `return` in the `get` parameter. I still have some problem related to the code, as I get this error: `Error: Cannot find control with path: 'textoAvisos -> [object Object]'`. I am trying to fix this mistake as of now. – Leminur Oct 31 '18 at 13:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/182866/discussion-between-willwsharp-and-sem-ideia). – Willwsharp Oct 31 '18 at 14:02