質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Angular

Angularは、JavaScriptフレームワークです。AngularJSの後継であり、TypeScriptベースで実装されています。機能ごとに実装を分けやすく、コードの見通しが良いコンポーネント指向です。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

1回答

7770閲覧

【Angular】constructorとngOnInitの違いについて(初期化エラー)

tonkotsu_ramen

総合スコア6

Angular

Angularは、JavaScriptフレームワークです。AngularJSの後継であり、TypeScriptベースで実装されています。機能ごとに実装を分けやすく、コードの見通しが良いコンポーネント指向です。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

0クリップ

投稿2021/08/22 11:39

編集2021/08/22 11:45

状況

現在、Angularチュートリアル等でフォームについて学習している初学者です。
constructorとngOnInitの違いについて、なかなか腑に落ちないので、アドバイスいただければ幸いです。

やろうとしていること

こちらのリンクのカスタムバリデーションを自分のローカル環境でも実装してみました。
https://stackblitz.com/edit/angular-u1a86m?file=src%2Fapp%2Fapp.component.ts

とりあえず、完コピしてみたのですが、Property 'form' has no initializer and is not definitely assigned in the constructor.というエラーが出てしまいます。

コンストラクターで初期化出来ていないというエラーだと思い、ngOnInit()に書かれている処理を全てconstructorに移動しました。そうすると、問題なく動作し、上記リンクとstackblitzと同じ挙動で動いています。

コード

typescript

1// test.component.ts 2import { Component, OnInit } from '@angular/core'; 3import { 4 FormGroup, 5 FormControl, 6 FormBuilder, 7 Validators, 8 AbstractControl, 9} from '@angular/forms'; 10@Component({ 11 selector: 'app-test', 12 templateUrl: './test.component.html', 13 styleUrls: ['./test.component.scss'], 14}) 15export class TestComponent implements OnInit { 16 form: FormGroup; 17 18 constructor(private _fb: FormBuilder) { 19 this.form = this._buildForm(); 20 } 21 22 ngOnInit() { 23 // this.form = this._buildForm(); stackblitzはここで初期化している。 24 } 25 26 submit(form: FormGroup) { 27 console.log(form); 28 } 29 30 get phonenumber() { 31 return <FormControl>this.form.get('phonenumber'); 32 } 33 34 private _buildForm(): FormGroup { 35 return this._fb.group({ 36 phonenumber: ['', [Validators.required, parseToNumberValidator]], 37 }); 38 } 39} 40function parseToNumberValidator(formControl: AbstractControl) { 41 const val = formControl.value; 42 return isNaN(Number(val)) ? { parseToNumberValidator: true } : null; 43} 44

typescript

1// test.component.ts 2<p>Start editing to see some magic happen :)</p> 3<form [formGroup]="form" (submit)="submit(form)"> 4 <div> 5 <label> 6 phone number 7 <input type="text" formControlName="phonenumber" *ngIf="phonenumber" /> 8 </label> 9 <ng-container 10 *ngIf="phonenumber.invalid && (phonenumber.touched || phonenumber.dirty)" 11 > 12 <p *ngIf="phonenumber.hasError('required')">必須項目です</p> 13 <p *ngIf="phonenumber.hasError('parseToNumberValidator')">半角数字のみ</p> 14 </ng-container> 15 </div> 16 <div> 17 <button type="submit">send</button> 18 </div> 19</form> 20

わからないこと

・なぜ、上記リンクのstackblitzはconstructorで初期化していないのにエラーがでないのか。
(自分のローカル環境だけエラーになるのはなぜか)
・ngOnInitとconstructorをどう使い分けたらよいかわからない。

環境的な要素が原因で、エラーとなっているのでしょうか。
ng newで新規プロジェクトを作成し、同様のことをしてもやはり同じ状況になってしまいます。
どういったところに原因がありそうか、アドバイス頂けると幸いです。
(teratail内で似たような質問をしてしまっています…すみません…)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ts.config.json の設定ではないかと思います。
ts.config.json の "angularCompilerOptions"
"strictPropertyInitialization": true
を指定していると、 formngOnInit で初期化した際にエラーになります。

Angular におけるコンポーネントクラスの処理順は、
1.constructor 実行、フィールド変数の初期化
2.親コンポーネントから @Input を渡す
3.ngOnInit 実行
4.コンポーネントの描画
5.ngAfterViewInit 実行
となっています。( ngOnChanges については省略)
Angular のコンポーネントの初期化処理を ngOnInit で行うように推奨されているのは、上記2によるところが大きいです。
つまり、親コンポ―ネントから値を渡す場合と渡さない場合で、初期化処理を constructor に書くのか ngOnInit に書くのか分けるのはあまりよくないよね、それなら親から値を渡してようが渡してまいが、 ngOnInit に書けば統一が取れるよね、という考えです。

次に ts.config.json では TypeScript をトランスパイルする設定を記述しています。
strictPropertyInitialization オプションはその名の通り、コンストラクタ実行後に初期化されてないフィールド変数を許す/許さないの設定です。
Angular12 からは Angular CLI で ng new するとデフォルトで Strict モードが true になるようになりました。Strict モードが true のとき、strictPropertyInitialization オプションは有効です。フィールド変数がコンストラクタ実行後に初期化されていない場合はエラーになります。

提示されている stackblitz の Angular バージョンは7です。まだデフォルトで Strict モードは false です。
対して質問者様の Angular バージョンは12以上なのではないでしょうか?(または ng new 時に --strict を指定しているか)
この環境の違いが、エラーが発生する/しないを左右しているのだと思います。

解決方法としては、フィールド変数 form の記述を変更することです。

方法1 form?: FormGroup にする
省略可能なプロパティとしてしまえば、strictPropertyInitialization オプションが有効になっていてもエラー発生しません。
ngOnInit で初期化することができます。

方法2 form!: FormGroup にする
こちらの方が有力です。Non-Nullアサーションパラメータを使って、Null にはならないことを明示します。
こちらもstrictPropertyInitialization オプションが有効になっていても ngOnInit で初期化することができます。
今回の場合、 form が NULL になることはないのでこちらの方が適切です。

投稿2021/08/22 13:56

gekijin

総合スコア187

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tonkotsu_ramen

2021/08/22 14:16

ご丁寧にご回答頂きありがとうございます! こちらからもいくつか回答させていただきます。 ・angularのバージョンは12です。 ・書き漏れていましたが、`strictPropertyInitialization `は`false`にしていても状況変わりませんでした。 上記の方法2でエラーは解消されました。angularのコンポーネントの処理手順について、もっと深掘りして勉強してみます。本当にありがとうございます。
gekijin

2021/08/22 14:26

・書き漏れていましたが、`strictPropertyInitialization `は`false`にしていても状況変わりませんでした。 おや、私の環境ではこのオプションを変更すると明確に挙動に違いが出ます。 tsconfig.json を書き換えた後は、一旦 ng serve を止めて ng serve をやり直さないと変更が反映されないようですが、いかがでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問