I’m working on a form that manages currencies, with fields for code and label. I’m using the latest Angular features and trying to leverage typed forms.
I have a FormArray
inside a FormGroup
, which is populated with some pre-filled currency data. The reactive form works as expected—I can add and remove currencies with buttons.
However, I’m facing an issue when trying to access the form control properties in my HTML template to apply validation classes (e.g., showing a valid or invalid state for the input fields). I receive the following error:
NG9: Property 'code' does not exist on type 'AbstractControl<any, any>'. [plugin angular-compiler]
src/app/countries.component.html:34:58:
34 │ [class.is-valid]="currency.code.valid"
Problem:
Here’s the code that triggers the error:
[class.is-valid]="currency.code.valid"
I’ve also tried changing it to:
[class.is-valid]="currency.controls['code'].valid"
Or:
[class.is-valid]="currency.get('code').valid"
But I still get the same error.
My TypeScript Code:
import { CommonModule } from '@angular/common';
import { Component, inject, OnInit } from '@angular/core';
import {
FormArray,
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators
} from '@angular/forms';
export interface Item {
code: string;
label: string;
}
@Component({
selector: 'app-countries',
standalone: true,
imports: [ReactiveFormsModule, CommonModule],
templateUrl: './countries.component.html',
styleUrl: './countries.component.scss'
})
export class CountriesComponent {
currencyForm: FormGroup;
private formBuilder = inject(FormBuilder);
constructor() {
this.currencyForm = this.formBuilder.group({
currencies: this.formBuilder.array<FormGroup>(
[
{code: 'CAD', label: 'Canadian Dollar'},
{code: 'USD', label: 'United States Dollar'},
{code: 'EUR', label: 'Euro'}
].map(currency => this.createCurrencyFormGroup(currency))
)
});
}
createCurrencyFormGroup(currency: Item) {
return this.formBuilder.group({
code: [currency.code, [Validators.required, Validators.maxLength(3), Validators.minLength(3)]],
label: [currency.label, Validators.required]
});
}
get currencies(): FormArray {
return this.currencyForm.get('currencies') as FormArray;
}
addCurrency() {
const currencyControl = this.createCurrencyFormGroup({code: '', label: ''});
this.currencies.push(currencyControl);
}
removeCurrency(index: number) {
this.currencies.removeAt(index);
}
}
My HTML Code:
<div class="container mt-3">
<form [formGroup]="currencyForm" class="row g-3">
<div class="col-md-5" formArrayName="currencies">
<button type="button" class="btn btn-sm btn-secondary" (click)="addCurrency()">Add Currency</button>
<div *ngFor="let currency of currencies.controls; let idx = index" [formGroupName]="idx">
<div class="row">
<div class="col-4">
<input type="text"
class="form-control"
placeholder="Currency Code"
formControlName="code"
maxlength="3"
[class.is-valid]="currency.get('code').valid"
[class.is-invalid]="currency.get('code').invalid" />
</div>
<div class="col-6">
<input type="text"
class="form-control"
placeholder="Currency Label"
formControlName="label"
[class.is-valid]="currency.get('label').valid"
[class.is-invalid]="currency.get('label').invalid" />
</div>
<div class="col-2">
<button type="button" class="btn btn-danger" (click)="removeCurrency(idx)">Remove</button>
</div>
</div>
</div>
</div>
</form>
</div>
What I've Tried:
currency.code.valid
, currency.controls['code'].valid
and currency.get('code').valid
, but both result in the same error.Question:
How can I properly access the form control properties in my FormArray
to apply validation classes in the template? What am I doing wrong with my approach to accessing the code
and label
properties?
Thank you so much
TRY
Use (see that it's not only a FormArray else a FormArray of FormGroup
get currencies(): FormArray {
return this.currencyForm.get('currencies') as FormArray<FormGroup>;
}
And use (see the '?')
[class.is-valid]="currency.get('code')?.valid"
[class.is-invalid]="currency.get('code')?.invalid"
If not work, Always can use the "dot" notation to reach the control
[class.is-valid]="currencyForm.get('currency.'+idx+'.code)?.valid"
[class.is-invalid]="currencyForm.get('currency.'+idx+'.code)?.invalid"
NOTE: You needn't use always a FormArray inside a FormGroup
You can use, e.g.
currencies = this.formBuilder.array<FormGroup>(
[
{ code: 'CAD', label: 'Canadian Dollar' },
{ code: 'USD', label: 'United States Dollar' },
{ code: 'EUR', label: 'Euro' },
].map((currency) => this.createCurrencyFormGroup(currency))
);
And
<div *ngFor="let currency of currencies.controls; let idx = index"
[formGroup]="currency">
<input .. formControlName="code"/>
<input .. formControlName="label"/>
</div>