# Day30 【牙起來】 表單(Form)初始化 - Angular
常見Angular的表單分為兩大類:
* Dynamic Form 動態表單
* Reactive Form 響應式表單
## Dynamic Form 動態表單
---
在開始之前,首先先產生一個`user-input`讓使用者可以輸入資料的元件
> ng g c user-input
## Reactive Form 響應式表單
首先修改`app.module.ts`,因為使用到響應式表單,需匯入`ReactiveFormsModule`模組
```typescript=
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { UserInputComponent } from './user-input/user-input.component';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent,
UserInputComponent
],
imports: [
BrowserModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
```
在程式中新增`myForm`表單,由`FormGroup`包住`FormControl`
`FormControl` 表單控制項,可以視作**表單欄位**,可以輸入欄位預設值
`FormGroup` 是表單群組,可以視作**一整個表單**,底下可包含多個表單欄位
修改 `user-input.component.ts`
```typescript=
import { FormControl, FormGroup } from '@angular/forms';
...
export class UserInputComponent implements OnInit {
myForm = new FormGroup({
name: new FormControl('勇者一號'),
age: new FormControl(19),
});
constructor() { }
ngOnInit(): void {
}
getData() {
console.log(this.myForm.get('name')?.value, this.myForm.get('age')?.value);
}
}
```
> * `FormGroup`
> * `FormControl`
> * `AbstractControl`
>
> 因以上的Form相關元素不是Service,而是**物件**
> 所以使用時需透過`import`後`new`一個實例來產生新物件
>
> ```typescript=
> ...
> import { FormGroup } from '@angular/forms';
> ...
>
> export class UserInputComponent implements OnInit {
>
> formGroup = new FormGroup({
>
> })
>
> ...
> ```
>
> **而非**以下注入服務的方式
>
> ```typescript=
> ...
> export class UserInputComponent implements OnInit {
>
> constructor(public formGroup: FormGroup) { }
>
> ...
> ```
接著在樣板上新增`formControlName`
修改 `user-input.component.html`
```html=
<form [formGroup]="myForm">
<div>名稱:<input type="text" formControlName="name"></div>
<div>年紀:<input type="text" formControlName="age"></div>
</form>
<button (click)="getData()">印出資料</button>
```
最後修改`app.component.html`
```html=
<app-user-input></app-user-input>
```
至此 `user-input` 元件可以告一個段落,可以開啟F12看效果
### FormBuilder 表單生成器
與前面`FormControl`**物件**不同,`FormBuilder`一個便於產生物件的**服務**
首先一樣宣告`myForm`成員,但不賦予初始值而只給型別
引入`FormBuilder`服務,可以省去前面範例落落長的英文字,在表單欄位很多的時候非常好用
修改 `user-input.component.ts`
```typescript=
import { FormBuilder, FormGroup } from '@angular/forms';
...
export class UserInputComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = fb.group({
name: '勇者一號',
age: 19,
});
}
...
```
或者在宣告成員的時候,使用constructor注入的`fb`服務,
直接創表單同時給予初始值
```typescript=
...
export class UserInputComponent implements OnInit {
myForm = this.fb.group({
name: '勇者一號',
age: 19,
});
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
}
}
```
可以達到與前面範例相同的效果
[property宣告 放construct內、外有何差異?](https://stackoverflow.com/questions/64436532/javascript-class-property-inside-vs-outside-constructor)
執行順序也相同,兩者無分先後
// TODO
[`[formControl]` vs `formControlName`](https://stackoverflow.com/questions/40171914/what-is-the-difference-between-formcontrolname-and-formcontrol)
三種取值方法

```typescript=
console.log(this.searchForm.value['code'], '111')
console.log(this.searchForm.get('code')?.value, '222')
console.log(this.searchForm.getRawValue().code, '333')
```