# 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

## 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的型別
有更好更嚴謹的作法
如果要想在表單中也有定義好的型別、以及跨元件的使用,方法會在下一章提及