---
# System prepended metadata

title: Day32 【牙起來】 - Angular 元件間傳值 Input、Output

---

# 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的型別

有更好更嚴謹的作法
如果要想在表單中也有定義好的型別、以及跨元件的使用，方法會在下一章提及

