get() メソッドを使用して "Property 'controls' does not exist on type 'AbstractControl'" エラーを解決
Angular 4 で発生する "Property 'controls' does not exist on type 'AbstractControl'" エラーの原因と解決策
このエラーは、Angular 4 で FormGroup または FormArray インスタンスに対して controls プロパティにアクセスしようとしたときに発生します。 TypeScript コンパイラは、AbstractControl
型のインスタンスには controls
プロパティが存在しないことを検出し、エラーを報告します。
このエラーの根本的な原因は、TypeScript の型システムと Angular テンプレートエンジン間の不一致にあります。 Angular テンプレートエンジンは、TypeScript 型情報にアクセスできません。そのため、テンプレート内で controls
プロパティに直接アクセスしようとすると、コンパイラがエラーを報告します。
このエラーを解決するには、以下の2つの方法があります。
get() メソッドを使用する
FormGroup
と FormArray
インスタンスには、get()
メソッドが用意されています。このメソッドを使用して、特定のコントロールにアクセスできます。
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
上記のコードでは、myFormGroup.controls
イテレータを使用して、FormGroup
内のすべてのコントロールをループしています。各ループイテレーションで、control.key
プロパティを使用して、コントロールの名前を取得し、formControlName
ディレクティブにバインドしています。
カスタムアクセサーを作成する
controls
プロパティに直接アクセスする代わりに、カスタムアクセサーを作成して、FormGroup
または FormArray
インスタンスのプロパティとして公開することができます。
export class MyFormGroup extends FormGroup {
get controls() {
return this.value.controls;
}
}
上記のコードでは、MyFormGroup
というカスタム FormGroup
クラスを作成しています。このクラスには、controls
というプロパティが定義されています。このプロパティは、FormGroup
インスタンスの value
プロパティの controls
プロパティを返すように実装されています。
テンプレート内で、MyFormGroup
インスタンスの controls
プロパティにアクセスすることができます。
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
どちらの方法を選択するかは、状況によって異なります。 get()
メソッドを使用する方法は、シンプルでわかりやすい方法です。一方、カスタムアクセサーを作成する方法は、より柔軟性と制御性を提供します。
補足
- このエラーは、Angular 4 以降のバージョンでも発生する可能性があります。
- このエラーを解決するには、必ず TypeScript 2.4 以降を使用していることを確認してください。
Angular 4 で "Property 'controls' does not exist on type 'AbstractControl'" エラーを解決するサンプルコード
このサンプルコードでは、get() メソッドとカスタムアクセサーの両方を使用して、FormGroup インスタンスの controls プロパティにアクセスする方法を示します。
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
`
})
export class AppComponent {
myFormGroup = new FormGroup({
firstName: new FormControl('John'),
lastName: new FormControl('Doe'),
email: new FormControl('[email protected]'),
});
}
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
`
})
export class AppComponent {
myFormGroup = new MyFormGroup({
firstName: new FormControl('John'),
lastName: new FormControl('Doe'),
email: new FormControl('[email protected]'),
});
}
export class MyFormGroup extends FormGroup {
get controls() {
return this.value.controls;
}
}
このサンプルコードは、以下の方法で実行できます。
- Angular CLI を使用して、新しい Angular プロジェクトを作成します。
- 上記のコードを
app.component.ts
ファイルに貼り付けます。 ng serve
コマンドを実行して、アプリケーションを実行します。
このサンプルコードを実行すると、以下の結果が表示されます。
<form>
<input type="text" formControlName="firstName">
<input type="text" formControlName="lastName">
<input type="text" formControlName="email">
</form>
Angular 4 で "Property 'controls' does not exist on type 'AbstractControl'" エラーを解決するその他の方法
上記で紹介した get() メソッドとカスタムアクセサー以外にも、"Property 'controls' does not exist on type 'AbstractControl'" エラーを解決する方法はいくつかあります。
型ガードを使用して、AbstractControl
インスタンスが FormGroup
または FormArray
であることを確認することができます。
import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
`
})
export class AppComponent {
myFormGroup = new FormGroup({
firstName: new FormControl('John'),
lastName: new FormControl('Doe'),
addresses: new FormArray([
new FormGroup({
street: new FormControl('123 Main St'),
city: new FormControl('Anytown'),
state: new FormControl('CA'),
zip: new FormControl('90210'),
}),
]),
});
get addresses() {
return this.myFormGroup.get('addresses') as FormArray;
}
}
上記のコードでは、addresses
プロパティの型ガードを使用して、addresses
プロパティが FormGroup
であることを確認しています。これにより、controls
プロパティに安全にアクセスすることができます。
import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of (myFormGroup.get('addresses') as FormArray).controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
`
})
export class AppComponent {
myFormGroup = new FormGroup({
firstName: new FormControl('John'),
lastName: new FormControl('Doe'),
addresses: new FormArray([
new FormGroup({
street: new FormControl('123 Main St'),
city: new FormControl('Anytown'),
state: new FormControl('CA'),
zip: new FormControl('90210'),
}),
]),
});
}
上記のコードでは、ダウンキャストを使用して、myFormGroup.get('addresses')
インスタンスを FormArray
に明示的に変換しています。これにより、controls
プロパティに安全にアクセスすることができます。
<form [formGroup]="myFormGroup">
<ng-container *ngFor="let control of myFormGroup.controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
<ng-container *ngFor="let control of (myFormGroup.get('addresses') as FormArray).controls | keyvalue">
<input type="text" formControlName="{{ control.key }}">
</ng-container>
</form>
angular typescript angular-material