Blog

ブログ

Angular講座:コンポーネント間の値受け渡し①

2021.10.18
Momoka_Ide
Momoka_Ide プログラマ

ブログ書こう書こう、と思いながら時間ばかり過ぎていく…

個人的にQiitaに学んだことをまとめていたりするのですが、
投稿から1以上年経っても未だに閲覧数やLGTMがたまに増えてる記事について、この一年で増えた知見も追記しつつ、紹介したいと思います。

お題は、タイトルの通り、
「Angularのコンポーネント間の値の受け渡しについて」

いまだに伸びてるのは、
おそらく初心者さんが悩んで検索することが多いのかな?と思うので、
初心者の方や弊社デザイナの方にも動かしながらすっと理解してもらえるよう噛み砕いて説明していけたらと思います。

コンポーネントとは?

検索すると
 ・構成要素。部品。コンピュータ機器やソフトウェアの部品。
…などと出てきます。
Angularでコンポーネントを作るときは、以下のコマンドで作りますね。

ng g c コンポーネント名

html、cssと、tsファイルが生成されます。
ng serveして作成したファイルのページを開くと、1つのページとして表示されます。
なので初心者の方はコンポーネント=ページのこと?っていうイメージが最初にできるのではないだろうか?

もちろんページとしての役割もあるのですが、あくまで「部品」
例えばhtmlはボタンだけのコンポーネント(app-button)と、そのボタンコンポーネントをhtmlの中に組み込んだコンポーネント(parent)があるとします

ng g c parent
ng g c parent/button

parent.component.html
<div class=”container m-5 p-3 bg-light”>
  コンポーネント1
<div>
<app-button></app-button>
</div>
</div>

button.component.html
<button type=”button” class=”btn btn-primary m-2″>QOX</button>

ルーティング

app-routing.module.ts
const routes: Routes = [ {path:””,component:ParentComponent}, ];

localhost:4200を表示すると、上記のように表示されます。
同じコマンドで作ったファイルたちですが、
ルートに登録したparentコンポーネントはページとして表示されるし、
parentの中で使ったbuttonは部品として役割を果たすわけです。
cssについてはbootstrapを使用していますので、コードは省略します。
もしbootstrapを利用しない場合は、サンプルと同じクラス名でクラスを作ってください。

コンポーネント間で値を受け渡したいときってどんなとき?

異なるコンポーネントを組み合わせてひとつのページが作れるんだな!
とわかったところで、例えば先程の親コンポーネントに、色が選べるラジオボタンがいくつかあったとしましょう。

parent.component.html
<div class=”container m-5 p-3 bg-light”>
  コンポーネント1
<div class=”m-2″>
<input type=”radio” id=”primary” value=”primary” checked><label for=”primary” class=”ml-2 mr-3″>primary</label>
<input type=”radio” id=”warning” value=”warning”><label for=”warning” class=”ml-2 mr-3″>warning</label>
<input type=”radio” id=”info” value=”info”><label for=”info” class=”ml-2 mr-3″>info</label>
</div>
<div>
<app-button></app-button>
</div>
</div>

ラジオボタンの選択が変わったときに、その色にボタンの色を変えたいとします。その場合、親コンポーネントは何色が選択されたのかを、ボタンコンポーネントに教えないといけません。
コンポーネントで何か操作したときに、他のコンポーネントに通知したり変更内容を伝えたりしたいときに受け渡しが必要になるわけです。

受け渡しする方法

いくつかあるよ。どれを使うかは、実装したいことに合わせて変えようね。
・Input
・Output
・service
・RxJS
・Storage
・urlに付与

今回はInputについて解説するよ。

Input

どんな時に使う?

上記のサンプルのように、親コンポーネントの中に子コンポーネントがある状態。
親コンポーネントから子コンポーネントに値を渡してあげたいとき。
それでは、ラジオボタンで選んだ色をボタンに反映させる例をInputを使って実装してみましょう

ng g c use-input
ng g c use-input/input-child

親コンポーネント

use-input.component.html
<div class=”container m-5 p-3 bg-light”>
コンポーネント1
<div class=”m-2″>
<form>
<input type=”radio” id=”primary” name=”selectColor” value=”btn-primary” checked [(ngModel)]=”colorClass”>
<label for=”primary” class=”ml-2 mr-3″>primary</label>
<input type=”radio” id=”warning” name=”selectColor” value=”btn-warning” [(ngModel)]=”colorClass”>
<label for=”warning” class=”ml-2 mr-3″>warning</label>
<input type=”radio” id=”info” name=”selectColor” value=”btn-info” [(ngModel)]=”colorClass”>
<label for=”info” class=”ml-2 mr-3″>info</label>
</form>
</div>
<div>
<app-input-child [color]=”colorClass”></app-input-child>
</div>
</div>

use-input.component.ts
export class UseInputComponent implements OnInit {
colorClass:string=”btn-primary”;
constructor() {}
ngOnInit(): void {}
}

子コンポーネント

input-child.component.html
<button type=”button” class=”btn m-2″ [class]=”color”>QOX</button>

input-child.component.ts
export class InputChildComponent implements OnInit,OnChanges {
@Input() color:string = “btn-primary”; // <- Inputを使う!
constructor() {}
ngOnInit(): void {}
ngOnChanges(){ // 入力値が変わったときに何か処理したいときはOnChangesで
console.log(this.color);
}
}

bootstrapを利用しない場合はvalueに設定されているbtn-primaryなどの、ボタンの背景を指定するクラスを子コンポーネントのcssファイルに作成してください

ルーティング

app-routing.module.ts
const routes: Routes = [
{path:””,component:ParentComponent},
{path:”input”,component:UseInputComponent},
];

localhost:4200/input にアクセスしてみてください。

ラジオボタンを操作するとその選んだクラスの色にボタンの色が変わりました。

Inputを使うときに気をつけること

Inputを使ったとき親から子へ値を伝えますが、子から親へは本来情報は伝わりません(双方向でない)
ですが、一見双方向に見える書き方があります。
Inputで渡す値がオブジェクトだったとき、
値は参照渡しになっており、子で値を変更したときに親の値も変更されます。
予期せぬバグの元になるので、子から親に値を渡したいときはOutputを使うことを心がけましょう…!(アウトプットについては、また次回の更新で)

オブジェクト…{name:”taro”,age:”10″}という形に保存されているデータなど

当時はよくわかってなかったけど今思うと某プロジェクトで頭捻っていた時の原因これなのではないだろうか…

この事例のわかりやすかったサンプルがある記事の紹介。
【ionic】@InputでObjectをバインドするといろいろハマる【Angular】

Inputについては以上です。また時間のあるときに続きを書きたい(いつになることやら)
・Input(この記事)
・Output
・service
・RxJS
・Storage
・urlに付与