# Day33 【牙起來】 - 跨元件使用表單form型別 接續上一章 今天的目標是讓IDE能夠在B元件透過`.`點點來呼風喚雨,叫出底下的物件來 要讓程式做到型別推斷 ![](https://i.imgur.com/Wik7jTS.png) ## A元件定義型別 首先我們先拿掉所有的表單驗證器 然後在`user-input.component.ts`新增介面(Interface) ```typescript= ... export class UserInputComponent implements OnInit { @Output() public userFormChange = new EventEmitter<FormGroup>(); myForm = this.fb.group<Player>({ // 注意這邊的寫法,`<Player>`型別是這樣加上的 name: '勇者一號', age: 25, }) constructor(private fb: FormBuilder) { } ngOnInit(): void { this.userFormChange.emit(this.myForm); } formChanged() { this.userFormChange.emit(this.myForm); } } export interface Player { name: string age: number } ``` 這樣一來定義好表單的型別了 然後,接下來有幾種作法 ## (爛)方法一:直接加上 `| undefined` 給型別後,卻出現這樣的紅色底線波浪 ![](https://i.imgur.com/ebWMUpT.png) 修改父元件`app.component.ts` ```typescript= ... export class AppComponent { fv: Player | undefined; constructor() { } ... ``` 修改`calc-result.component.ts` ```typescript= ... export class CalcResultComponent implements OnInit { @Input() formValues: Player | undefined; constructor() { } ... ``` 然後因為`formValues`有可能為`undefined`,所以`calc-result.component.html`就要加上`?`、`!` * `?`: 如果有`formValues`這個物件的話,再執行這段程式 * `!`: 跟編譯器說,身為工程師我比你還更了解這個型別是什麼,給我乖乖閉嘴做事 ```html= {{formValues | json}} {{formValues?.age}} {{formValues!.age}} ``` ## 方法二:型別、初始值寫好寫滿 把`age`跟`name`都寫出來,並且給好初始值 ```typescript= ... export class AppComponent { fv: Player = { name: '', age: 0, } constructor() { } ... ``` > 也有冗長一點的寫法 > `new class implements ...` > > ```typescript= > ... > > export class AppComponent { > fv: Player = new class implements Player { > name = '勇者是你'; > age = 25; > }; > > constructor() { > } > ... > ``` 再對`calc-result.component.ts`依樣畫葫蘆 ```typescript= import { Component, Input, OnInit } from '@angular/core'; import { Player } from '../user-input/user-input.component'; @Component({ selector: 'app-calc-result', templateUrl: './calc-result.component.html', styleUrls: ['./calc-result.component.css'] }) export class CalcResultComponent implements OnInit { @Input() formValues: Player = { name: '勇者是你', age: 25, } constructor() { } ngOnInit(): void { } } ``` 再回到`calc-result.component.html`就可以直接使用了 ```html= {{formValues | json}} {{formValues.age}} ``` 只不過,寫初始值這件事情有點奇怪 畢竟都是接收方了,無論寫了什麼都會被蓋掉 而且滿滿的初始值宣告,當表單非常多個項目時,會讓程式碼變的無敵冗長 所以看情境使用 ## 方法三:善用as技巧 用型別關鍵字`as` > `數值 as 型別`做了什麼事情? > > 他打了太極拳 > 把原本不論何種型別的初始值,都化為另一種型別出去,給編譯器看 > 轉型之後,編譯器不會做檢查型別、跳提醒說屬性錯誤 修改`app.component.ts` ```typescript= ... export class AppComponent { fv = {} as Player; // 這樣子寫也是同樣意思 // fv = <Player>{}; constructor() { } ... ``` 修改`calc-result.component.ts` ```typescript= ... export class CalcResultComponent implements OnInit { @Input() formValues = {} as Player; // 這樣子寫也是同樣意思 // @Input() formValues = <Player>{}; constructor() { } ngOnInit(): void { } ... ``` 太極大法好阿 ## 方法四:將tsconfig修改成 不需給定初始值 就是不想給,將初始值完全省略掉 關閉ts的 **嚴格空檢查** 修改`app.component.ts` ```typescript= ... export class AppComponent { fv: Player; constructor() { } ... ``` 修改`calc-result.component.ts` ```typescript= ... export class CalcResultComponent implements OnInit { @Input() formValues: Player; constructor() { } ngOnInit(): void { } ... ``` 修改`tsconfig.json`編譯設定 在`compilerOptions`底下新增`"strictPropertyInitialization": false,` ![](https://i.imgur.com/OXCVh1a.png) 修改完後最好是關閉、重新`ng serve`一次比較不會遇到問題 --- --- --- ## 加上表單驗證器的型別 新增Interface,使用`value`、`controls`並且繼承`FormGroup` 繼承`FormGroup`是為了確保可以取用`FormGroup`底下的方法 ```typescript= export class UserInputComponent implements OnInit { @Output() public resultChanged = new EventEmitter<string>(); form = this.fb.group({ electricity: [0], water: [0, Validators.max(9999)], naturalGas: [{value: 0, disabled: false}, Validators.min(0)], gas: [{value: 0, disabled: true}, Validators.min(0)], }) as ItemGroup; ... export interface Item { electricity: number water: number naturalGas: number gas: number } export interface ItemGroup extends FormGroup { value: Item; // 型別檢查Item controls: { electricity: AbstractControl; // or FormControl water: AbstractControl; naturalGas: AbstractControl; gas: AbstractControl; } } ``` ![](https://i.imgur.com/YY0g4Ic.png) 而且在html可以這樣取用 // TODO 改以上範例 ## 泛型 // TODO https://ithelp.ithome.com.tw/articles/10278375 https://ithelp.ithome.com.tw/articles/10223810