Angular+Ionic4での子コンポーネントの作成とデータの受け渡し方法

##はじめに
今回プロジェクトでIonic４というGUIフレームワークを使用することになりました。
コンポーネントの作成やデータの受け渡しというところで記述する箇所がけっこう多かったのでメモに残します。

##使用環境
- Angular８
- Ionic4

##前提
Ionic CLIが使用できる状態。Ionic公式やQiita記事を参考にしてください。

- [Ionic公式](https://ionicframework.com/jp/docs/installation/cli)
- [Ionicの開発環境構築してみる](https://qiita.com/taketakekaho/items/1d69e80aa6799f3babb5)

##プロジェクトの作成

プロジェクトを作成するディレクトリまで移動してプロジェクトを作成します。
今回はタブ付きのテンプレートプロジェクトを作成します。

```
ionic start myApp tabs
```

JSフレームワークはAngularを選択。

```
? Framework:
❯ Angular | https://angular.io
  React   | https://reactjs.org
```
雛形は`tabs`の他に`sidemenu`などもあります。使用できる雛形は`--list`オプションで確認できます。

```
ionic start --list
```

##アプリの起動
作成したプロジェクトのディレクトリに移動しアプリを起動する。

```
cd myApp
ionic serve
```

ブラウザが自動起動するのでスマホサイズで確認すると、３つのタブがある画面が作成されます。
各ページのヘッダー部分が共通しているので、この部分をコンポーネントとして切り出し、コンポーネントの呼び出しを行う作りに変更していきます。

![スクリーンショット 2019-12-08 14.32.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537165/59c0f16d-4086-6d33-2f2f-d0d672ad98d7.png)

##コンポーネントの作成

まずはionicのジェネレート機能使って親のページから使用する子コンポーネントを作成します。

```
ionic generate
```
コンポーネントを選択

```
? What would you like to generate?
  enum
  page
❯ component
  service
  module
  class
  directive
```
パスを指定

```
? Name/path of component: componenｔ/header
```
app下にcomponentフォルダとheaderの各種ファイルが自動生成されます。
Angularではhtml,scss,ts、あとテストファイルで1つのコンポーネントが構成されています。


各ヘッダー部分のデフォルトの作りはこうなっています。

```tab1.page.html
<ion-header>
  <ion-toolbar>
    <ion-title>
      Tab One
    </ion-title>
  </ion-toolbar>
</ion-header>
```
これを生成したヘッダーのhtmlファイルにコピペします。
ヘッダータイトル表示部分は親コンポーネントからのデータをバインドするため、テンプレート式に書き換えます。
さらに戻るボタンを加え、ページごとにボタンを出し分けるため`*ngIf`を記載します。

```header.component.html
<ion-header>
  <ion-toolbar>
    <ion-buttons *ngIf="showBackButton" slot="start">
      <ion-back-button defaultHref="/"></ion-back-button>
    </ion-buttons>
    <ion-title>
      {{headerTitle}}
    </ion-title>
  </ion-toolbar>
</ion-header>
```
次にtsファイルでテンプレートを作成した部分のプロパティを記述します。
`@Input()`デコレーターを付けることで親から子へと値が連携されるようになります。

```header.component.ts
import { Component, OnInit, Input } from '@angular/core';

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

  constructor() { }

  @Input() headerTitle: String;
  @Input() showBackButton: Boolean;

  ngOnInit() {}

}
```

これで子コンポーネント側の準備は完了です、次に呼び出す側の`tab1`を手直ししていきます。

##親ページからの呼び出し

使用するコンポーネントのselector名をタグにして記載します。今回は`header.component.ts`を見て`selector`を確認します。使用するコンポーネントのプロパティが属性となっているのでタイトルに表示する文字列と、戻るボタンの表示の真偽値を設定します。

```tab1.page.html
<app-header headerTitle="Parent Tab One" showBackButton=true></app-header>
<ion-content>
　・・・
```

ヘッダーコンポーネントをインポートして、`declarations `に追記します。
使うモジュールは`declarations `に記述する必要があります。

```tab1.module.ts
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab1Page } from './tab1.page';
import { HeaderComponent } from '../component/header/header.component';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    RouterModule.forChild([{ path: '', component: Tab1Page }])
  ],
  declarations: [Tab1Page, HeaderComponent]
})
export class Tab1PageModule {}
```

ヘッダー部分が親が指定した文字列が連携できていることが確認できます。
他のページでもヘッダー部分をコンポーネントの呼び出しに書き換えれば、各ページで作っていたヘッダー部分のコンポーネント化が完了です。

![スクリーンショット 2019-12-08 14.32.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537165/8d634d8d-fe8d-22a4-baeb-a13351e39f84.png)

####****Component is part of the declarations of 2 modulesについて
複数のコンポーネントで使用する場合にエラーが発生します。これについて記事を書きました。
[Qiita -Error: Type ****Component is part of the declarations of 2 modulesの解消 -共有モジュールの作成](https://qiita.com/yunity29/items/3b7f3e7db8c2cc78191f)

##おわり
Angularは他のフレームワークと比べ学習コストが高いと言われていますが、こういう基本的なところではまだ学習コスト高いと感じません。
やっぱりDI（依存性の注入）とかかとっつきづらい感じなんでしょうか。

##共に働くWebエンジニアを募集しています!
不動産SHOPナカジツでは自社サービスを作っていく仲間を募集しています。
詳しくは[Wantedly](https://www.wantedly.com/projects/134741)からお問い合わせください。