child component 생성
상세화면이 heroes 컴포넌트안에 들어가 있는데 이것을 별도의 컴포넌트로 분리한다.
상세 화면을 보여줄 hero-detail 컴포넌트를 생성한다.
1 |
ng g component hero-detail |
hero-detail.component에 heroes의 상세화면을 잘라내서 그대로 넣는다.
1 2 3 4 5 6 7 8 9 10 11 |
<div *ngIf="selectedHero"> <h2>{{ selectedHero.name | uppercase }} Details</h2> <div><span>id: </span>{{selectedHero.id}}</div> <div> <label>name: <input [(ngModel)]="selectedHero.name" placeholder="name"> </label> </div> </div> |
selectedHero 라는 변수가 선언이 안되어있기 때문에 에러가 날것이다. selectedHero라는 변수는 부모 컴포넌트에서 받아와야 하므로 input이라는 데코레이션을 사용하였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Component({ selector: 'app-hero-detail', templateUrl: './hero-detail.component.html', styleUrls: ['./hero-detail.component.scss'] }) export class HeroDetailComponent implements OnInit { @Input() selectedHero: Hero; constructor() { } ngOnInit() { } } |
이제 부모 컴포넌트 hero 에서 selectedHero 값을 넘겨주자.
1 2 3 4 5 6 7 8 9 |
<h2>My Heroes</h2> <ul> <li class="d-flex m-1" *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero===selectedHero"> <span class="rounded-left p-2 bg-primary text-light">{{hero.id}}</span> <div class="rounded-right p-2 bg-light text-dark w-50">{{hero.name}}</div> </li> </ul> <app-hero-detail [selectedHero]="selectedHero"></app-hero-detail> |
scss도 동일하게 잘라내서 hero-detail로 옮긴다.
코드는 이전과 동일하게 동작해야 한다. 변화된 것은 부모 컴포넌트에서 자식컴포넌트가 독립해서 나온것이다. 컴포넌트를 잘게 쪼갤수록 확장성과 재사용성이 강해진다.
component interaction
상태 관리 | vue | react | angular |
parent => child | props | props | property binding |
parent <= child | emit | event | emit |
컴포넌트의 위치에 상관없이 통신할때
component <-> component |
emit, on
vuex |
redux | ReactiveX
Subject.next(), Observable.subscire() |
root에서 G로 상태를 전달할려면 어떻게 해야 하나? root -> A -> E -> G 로 props를 전달해야하는 번거로움이 생긴다.
store를 두고 subscribe, dispatch로 상태를 관리하면 간편해진다.
기본적인 개념은 GoF 디자인패턴의 Observer 패턴이다.
Example – votetaker and voter
1 2 3 |
ng g component votetaker ng g component voter |
자식 컴포넌트인 voter는 찬성, 반대 두가지중 하나를 투표할수 있는 agree라는 변수를 갖고 있다고 가정하자. voter가 3명이라고 한다면 3명이 각각 투표를 했다고 하면 찬성이 몇명이고 반대가 몇명인지 어떻게 알수 있을까? 자식 컴포넌트가 이 변수 값을 갖고 있으면 자식 입장에서는 각각의 값을 알 수 있지만 전체 숫자를 파악할수는 없다. 그러므로 이런 경우에는 부모가 이 값을 갖고 있어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Component({ selector: 'app-voter', templateUrl: './voter.component.html', styleUrls: ['./voter.component.scss'] }) export class VoterComponent implements OnInit { @Input() name: string; @Output() voted = new EventEmitter<boolean>(); didVote = false; vote(agree: boolean) { this.voted.emit(agree); this.didVote = true; } } |
voter.component.html
1 2 3 |
<h3>{{name}}</h3> <button class="btn btn-success" (click)="vote(true)" [disabled]="didVote">Agree</button> <button class="btn btn-danger" (click)="vote(false)" [disabled]="didVote">DisAgree</button> |
부모 컴포넌트를 보자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Component({ selector: 'app-votetaker', templateUrl: './votetaker.component.html', styleUrls: ['./votetaker.component.scss'] }) export class VotetakerComponent implements OnInit { agreed = 0; disagreed = 0; voters = ['Mr. Hong', 'Miss. Kim', 'Mr. Lee']; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; } } |
html은
1 2 3 4 |
<h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3> <app-voter *ngFor="let voter of voters" [name] = "voter" (voted)="onVoted($event)"> </app-voter> |
app.component.html 아래에 코드를 테스트해보자
1 2 3 |
<app-heroes></app-heroes> <app-votetaker></app-votetaker> |
[자식 property] = “부모 property”
(자식 event callback method) = “부모 callback method” => 자식 컴포넌트에서 method를 emit()하여 부모 메서드를 호출