# Day32 【牙起來】 - Angular 元件間傳值 Input、Output 傳值不是只有A元件傳給B元件就完結了事了 在這個章節會有三樣東西: * A元件(子元件) * B元件(子元件) * 同時使用到AB元件的樣板(父元件) 我們接下來要做的事情是,將A元件的值或事件 透過父元件樣板 來傳給B元件 那A與B是獨立分開的兩個元件,那還包含了一件事情,B元件接收到值何時刷新? 會透過事件驅動來傳遞**刷新的時間點** --- 事件發射器 A搭載事件發射器,當使用者在輸入框按下鍵盤時,就向外噴射事件,朝父元件一直噴 被噴得滿臉之後,父元件就會執行對應的方法 看是要去噴B元件還是做一些其他處理 ## 從A元件發射(事件繫結) 先來到A元件 修改`user-input.component.ts` ```typescript= import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-user-input', templateUrl: './user-input.component.html', styleUrls: ['./user-input.component.css'] }) export class UserInputComponent implements OnInit { // 使用`@Output()`代表這個成員是向外傳遞,傳遞一個事件物件 @Output() public userFormChange = new EventEmitter<FormGroup>(); myForm = this.fb.group({ name: ['勇者一號', Validators.required], age: [25, [Validators.required, Validators.min(5), Validators.max(120)]], }) constructor(private fb: FormBuilder) { } ngOnInit(): void { this.userFormChange.emit(this.myForm); // 在初始化的時候也傳遞初始值 } formChanged() { this.userFormChange.emit(this.myForm); } } ``` 修改`user-input.component.html` 掛上`formChanged()`方法,在按下任意按鍵時會執行 ```html= <form [formGroup]="myForm" (input)="formChanged()"> <label>冒險者名稱:<input type="text" formControlName="name"></label> <label>年紀:<input type="number" formControlName="age"></label> </form> ``` ## 打到父元件臉上,父元件接收到事件 接著來到父元件 修改`app.component.html`,新增父元件自己的`formChange`方法 ```html= <h1>A元件</h1> <app-user-input (userFormChange)="formChange($event)"></app-user-input> <hr> ``` 修改`app.component.ts` 新增`fv`成員以及`formChange`方法 接收好從事件傳來的值Form Value ```typescript= import { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { fv: Object = {}; constructor() { } formChange(fg : FormGroup) { this.fv = fg.value; console.log(this.fv) } } ``` 這樣一來就能在更改表單任意數值時(子元件),(父元件)接收的到傳過來的form value了 打開F12看一下運行結果 ## 父元件傳遞給身上的B元件(屬性繫結) 在往下繼續之前,先產生一個`calc-result`的元件,用來計算使用者輸入之資料的元件 > ng g c calc-result 接著修改`app.component.html` 將`fv`透過屬性繫結傳遞給子元件 ```html= <h1>A元件</h1> <app-user-input (userFormChange)="formChange($event)"></app-user-input> <hr> <h1>B元件</h1> <app-calc-result [formValues]="fv"></app-calc-result> ``` 此時`[formValues]`會有紅底波浪,因為找不到對應Property ![](https://i.imgur.com/ocWtImp.png) ## B元件跟著改變 來到 `calc-result.component.ts`中 將Property`formValues`定義好,加上`@Input()`代表是接收進來 表示此成員的值是從外部進來 ```typescript= import { Component, Input, OnInit } from '@angular/core'; @Component({ selector: 'app-calc-result', templateUrl: './calc-result.component.html', styleUrls: ['./calc-result.component.css'] }) export class CalcResultComponent implements OnInit { @Input() formValues: Object = {}; // 或者 @Input() formValues: any; constructor() { } ngOnInit(): void { } } ``` 在`calc-result.component.html`上添加便可看出效果 ```html= {{formValues | json}} ``` 確實有接收到來自A元件的更動 這樣子整套運作就完成拉 > 可以看到Angular元件間傳遞數值有多麼複雜 > > 我只是想要從A元件傳值到B元件而已 > 最終加了一堆東西才達到 美中不足的是,到目前的作法是用`{}`物件、`any`型別來接收資料 這沒辦法在樣板上直接透過`.`叫出物件底下的型別來,因為缺少form的型別 有更好更嚴謹的作法 如果要想在表單中也有定義好的型別、以及跨元件的使用,方法會在下一章提及