---
# System prepended metadata

title: Day30 【牙起來】 表單(Form)初始化 - Angular

---

# 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)


三種取值方法

![](https://i.imgur.com/w9Pcq4e.png)

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

