---
# System prepended metadata

title: "\U0001F954從認識 Component 開始"
tags: [Angular]

---

###### tags: `Angular`
# 🥔從認識 Component 開始
在開發專案的時候，大部分的程式邏輯我們都是寫在 Component 裡面 ，因此我想大家對於 Component 一定不陌生，但有些東西可能從來沒有注意過，或是不知道為什麼它是這個樣子。

所以我今天會從 Component 來開始，來帶大家認識 Angular 的一些基礎概念。

>[API參考手冊-Component](https://angular.tw/api/core/Component)。

## *@Component.* 裝飾器
- 宣告的類別掛上`@Component`裝飾器，就表示此類別為一個元件。Angular的元件類別負責暴露資料，並透過資料繫結機制來處理絕大多數檢視的顯示和使用者互動邏輯。
- `@Component()`裝飾器是擴充`@Directive()`裝飾器，增加了一些與範本(`template`)有關的特性。
    
## *@Component.* 資料配置
- `@Component`裝飾器內的配置，決定此元件會與哪個範本(`template`)建立關聯，與其他相關的程式，以下是`Component`基礎配置。

    ```typescript=
    import { Component, OnInit } from '@angular/core';

    @Component({
      selector: 'app-first',
      templateUrl: './first.component.html',
      styleUrls: ['./first.component.css']
    })
    export class FirstComponent implements OnInit {

      constructor() { }

      ngOnInit() {
      }

    }
    ```

## *@Input*
- 負責接收資料
>**Parent傳'Hello'，Child接收並顯示**
>1. 建立一個child component並設計`@Input()`功能。
>2. 建立一個parent component使用child component，且使用child的`@Input()`。

*step1.* 建立Child component

==child.component.ts==
```typescript=
import { Component, Input, OnInit } from "@angular/core";

@Component({
  selector: "app-child",
  templateUrl: "./child.component.html",
  styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
  @Input() strShow: string;

  constructor() {}

  ngOnInit() {}
}

```

==child.component.html==
```htmlembedded=
<p>{{strShow}}</p>
```

*step2.* 建立Parent component

==parent.component.ts==
```typescript=
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  strInput  = 'Hello';

  constructor() { }

  ngOnInit() {
  }

}
```

==parent.component.html==
- 要使用child內的`@Input()`要在HTML內使用，且要用[中括號]括起來後面再帶入參數。
```htmlembedded=
<app-child [strShow]="strInput"></app-child>
```


## *@Output*
- 負責發射資料
>**Parent接收點擊按鈕後，由Child送出的'World'**
>1. 建立一個child component並設計`@Output()`功能。
>2. 建立一個parent component使用child component，且使用child的`@Output()`。

*step1.* 建立Child component

==child.component.ts==
- Output一定要搭配使用EventEimmter，這是固定寫法。
- emit()，就是發射訊息的意思。
```typescript=
import { Component, EventEmitter,  OnInit, Output } from "@angular/core";

@Component({
  selector: "app-child",
  templateUrl: "./child.component.html",
  styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
  @Output() strWord = new EventEmitter();

  constructor() {}

  ngOnInit() {}

  onClick() {
    this.strWord.emit('World');
  }
}
```

==child.component.html==
```htmlembedded=
<button (click)="onClick()">Output</button>
```
*step2.* 建立Parent component

==parent.component.ts==
```typescript=
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  showStrWord(strWord) {
    console.log(strWord);
  }

}
```

==parent.component.html==
- 要使用child內的`@Output()`要在HTML內使用，且要用(小括號)括起來後面再帶入參數。
- `showStrWord($event)`，這裡面的`$event`是emit的值，那$event也是固定寫法。
- 可以在devTools[F12] console 看到child傳送過來的值，確認是否正確。
```htmlembedded=
<app-child (strWord)="showStrWord($event)"></app-child>
```


## *其他傳值的方法*
[[T]Passing Parameters 參數傳遞](https://hackmd.io/@Ru/BkcCsfB2H#ViewChild)

- @ViewChild

==viewchild.component.html==
```htmlembedded=
<h2>@ViewChild()</h2>
<p>{{message}}</p>
```

==viewchild.component.ts==
```typescript=
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-viewchild',
  templateUrl: './viewchild.component.html',
  styleUrls: ['./viewchild.component.scss']
})
export class ViewchildComponent implements OnInit {
  message: string = '我是土豆';
  constructor() { }

  ngOnInit(): void {
  }

  setMessage(message: string) {
    this.message = message;
  }

}

```

==component.component.html==
```htmlembedded=
<app-viewchild #vc></app-viewchild>
<input [(ngModel)]="vcMsg" (change)="onChange()">
```

==component.component.ts==
```typescript=
import { ViewchildComponent } from './viewchild/viewchild.component';
import { Component, OnInit, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-component',
  templateUrl: './component.component.html',
  styleUrls: ['./component.component.scss']
})
export class ComponentComponent implements OnInit, AfterViewInit {
  @ViewChild('vc') vc: ViewchildComponent;

  vcMsg: string;
  constructor(
    private changeDetectorRef: ChangeDetectorRef
  ) { }
  
  ngOnInit(): void {
  }
  
  ngAfterViewInit(): void {
    this.vc.setMessage('土豆');
    this.changeDetectorRef.detectChanges();
  }

  onChange() {
    this.vc.setMessage(this.vcMsg)
  }

}

```

> `ExpressionChangedAfterItHasBeenCheckedError`
> 使用 `@ViewChild` 的時候需要注意父元件的使用時機


- RouterState

==router-state.component.html==
```htmlembedded=
<h2>routerState</h2>

<button routerLink="/a4/component/input" [state]="{page: 'A'}">Page A</button>
<button (click)="onClickB()">Page B</button>
<button routerLink="/a4/component/viewchild">Page C</button>
<router-outlet></router-outlet>
```

==router.state.component.ts==
```typescript=
import { Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-router-state',
  templateUrl: './router-state.component.html',
  styleUrls: ['./router-state.component.scss']
})
export class RouterStateComponent implements OnInit {

  constructor(
    private router: Router
  ) { }

  ngOnInit(): void {
  }

  onClickB() {
    this.router.navigate(['/a4/component/output'], {state: {page: 'B'}})
  }
}
```

==input / output component.ts==
```typescript=
ngOnInit(): void {
    console.log('state', window.history.state);
 }
```