0

So I have an Angular app using the dynamic forms, which works fine. but when I add --aot to my cli command I get the error Property 'controls' does not exist on type 'AbstractControl'. I've tried using methods from the documentation. Still I get error. My code attached below.Cause I've been through the documentation. Or maybe it's something I'm missing

import { Component, ElementRef, NgZone, OnInit, ViewChild } from '@angular/core'

    import { FormControl, ReactiveFormsModule, FormGroup, FormArray, FormBuilder, Validators } from "@angular/forms"
    import { Router, ActivatedRoute } from '@angular/router'
    import { Location } from '@angular/common'
    import { CreateEventService } from './create-event.service'
    import { Http, Headers } from '@angular/http'

      @Component({
        selector: 'create-event',
        templateUrl: './create-event.component.html',
        styleUrls: ['./create-event.component.css'],
        providers: [ CreateEventService ]
      })
      export class CreateEventComponent implements OnInit {

      event: any
      tables: string[] = []
      searchControl: FormControl
      ticketForm: FormGroup
      ticketPrices: number[] = []

      @ViewChild("search")
      public searchElementRef: ElementRef

      constructor(private router: Router,private _http: Http, private ngZone: NgZone, private _fb: FormBuilder) { }

      ngOnInit() {
        this.ticketForm = this._fb.group({
            tickets: this._fb.array([
                this.initTicket(),
            ])
        })
        this.searchControl = new FormControl()
      }

      initTicket() {
          return this._fb.group({
              ticketName: [''],
              ticketPrice: [''],
              ticketLimit: ['', Validators.required]
          })
      }

      addTicket() {
          const control = <FormArray>this.ticketForm.controls['tickets']
          control.push(this.initTicket())
      }

      removeTicket(i: number) {
          const control = <FormArray>this.ticketForm.controls['tickets']
          control.removeAt(i)
          this.tables.splice(i, 1)
          //console.log(this.tables)
      }

}
<form [formGroup]="ticketForm" novalidate>
          <div formArrayName="tickets" class="row">
              <div *ngFor="let ticket of ticketForm.controls.tickets.controls let i=index" class="row">
                  <div class="col m7 s12" [formGroupName]="i">
                    <div class="col m5 s12">
                        <input type="text" formControlName="ticketName" placeholder="e.g. Regular, VIP">
                    </div>
                    <div class="col m4 s6">
                        <input type="number" formControlName="ticketPrice" placeholder='Ticket Price &#8358'>
                    </div>
                    <div class="col m3 s6">
                      <input type="number" formControlName="ticketLimit" placeholder="Quantity">
                    </div>
                  </div>
                  <div class="col m2 s2">
                    <br>
                      <a class="primary-base-text" *ngIf="ticketForm.controls.tickets.controls.length > 1" (click)="removeTicket(i)"><i class="fa fa-trash"></i></a>
                  </div>
              </div>
          </div>
          <div class="col">
              <a style="float: left" (click)="addTicket()"><h6>Add another ticket <i class="fa fa-plus"></i></h6></a>
          </div>
</form>
tobie
  • 187
  • 10
  • 1
    Possible duplicate of https://stackoverflow.com/questions/45738257/property-controls-does-not-exist-on-type-abstractcontrol-angular4 essentially you create a function that returns your form array `getForm(){return this.ticketForm.controls.get('tickets')}` and use the function in your html. `getForm().controls` – LLai Nov 14 '17 at 21:32
  • @LLai I saw that question too, and it didn't help me – tobie Nov 14 '17 at 23:28

1 Answers1

0

When compiling Angular template with AOT compiler, the factories are created in Typescript, which keep the type hinting.

So, you said in your TS that ticketForm is a FormGroup. So you can access ticketForm.controls, which is a map of AbstractControls. Then, accessing ticketForm.controls.tickets will give you an AbstractControl.

If you look at the documentation you can see that AbstractControl does not have a controls attribute. That's why the AOT compiler trigger an error, because it adds typings to the HTML template.

Possible solution :

You can add a getter in your TS, to allow access to the tickets FormArray

get ticketsFormArray(): FormArray {
    return this.ticketForm.controls['tickets'] as FormArray
}

And use it in your template

<div *ngFor="let ticket of ticketsFormArray.controls let i=index" class="row">
...
</div>
Noémi Salaün
  • 4,866
  • 2
  • 33
  • 37
  • I tried this too before now, I got error `Cannot invoke an expression whose type lacks a call signature. Type 'AbstractControl' has no compatible call signatures` – tobie Nov 14 '17 at 23:20
  • Did you call your function `getTicketsFormArray` or did you create a getter `ticketsFormArray` ? – Noémi Salaün Nov 15 '17 at 09:29
  • Ah my bad, `FormGroup.controls` is an array, not a map. So just replace `this.ticketForm.controls.get('tickets')` by `this.ticketForm.controls['tickets']` and add a type casting. I have edited my code in the answer above – Noémi Salaün Nov 15 '17 at 14:45
  • so I tried it again, and the error left the cli so it compiled successfully, and moved into the browser console ![view error][https://i.imgur.com/gRJ2srG.png] – tobie Nov 15 '17 at 22:11
  • Can you make a plunker that reproduce your issue ? I'm trying to help you step by step, but I'm in the dark without code example :/ – Noémi Salaün Nov 15 '17 at 23:24
  • thanks so much for your help. I think I'll try using the docs to rewrite my code or just leave it s is without --aot – tobie Nov 18 '17 at 02:09