Ionic初心者向け入門

## はじめに

この記事は、最近フロントエンドでIonicを触る案件に参画したので、
簡単なTODOアプリを作成するために、大事そうなことをメモしたものをまとめたものです。

## TODOアプリのリポジトリ

https://github.com/yuta-ushijima/ionic-tutorial

## デフォルトで表示するページを指定する
<ion-tabs selectedIndex="2"></ion-tabs>

「selectedIndex=""」を使うと、指定したIndexがデフォルトページとして設定できる。
 Index指定なので、0が一番最初のページ。

devサーバーを動かす

```bash

$ cd プロジェクトディレクトリ
$ ionic serve
```

### ionicのプロジェクトを作成する
```bash

ionic start プロジェクト名 テンプレート名
```
 
### componentの追加

pageディレクトリ配下に、作成したいコンポーネントのディレクトリを作る

```bash

mkdir -p page/component_name
```

### app.module.tsにコンポーネントを登録する

※Ionicでは、フォルダ内にファイルを置くだけでは、使用できないので、
@NgModuleに追記する必要がある

```javascript

import { HomePage } from '../pages/home/home'; // コンポーネントのインポート
import { LoginPage } from '../pages/login/login'; // コンポーネントのインポート

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    LoginPage //ここにコンポーネントを追記する
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    LoginPage //ここにコンポーネントを追記する
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}
```

### コンポーネントをgenerateする
```bash

$ ionic g <component_name> <option>
```

#### オプション

`--no-module #モジュールが作られない`

#### 参考サイト
https://ionicframework.com/docs/cli/generate/

## Ionicで指定できるテンプレート


## 便利コマンド
```bash

ionic docs
```

リファレンスをターミナルから起動可能。

```bash

ionic info
```

### プロジェクトの内部情報を出力する。

```bash

Yutas-MacBook-Air:ionic_tutorial ushijimayuuta$ ionic info
✔ Gathering environment info - done!

Ionic:

   ionic (Ionic CLI)  : 4.1.1 (/usr/local/lib/node_modules/ionic)
   Ionic Framework    : ionic-angular 3.9.2
   @ionic/app-scripts : 3.2.0

Cordova:

   cordova (Cordova CLI) : not installed
   Cordova Platforms     : not available
   Cordova Plugins       : not available

System:

   NodeJS : v8.9.4 (/usr/local/bin/node)
   npm    : 6.2.0
   OS     : macOS High Sierra
```

## hoge.module.tsの正体

このモジュールファイルは、遅延読み込み(Lazy Loading(以下LL))を実現するために存在している。

Ionicは`index.html`一つで全てのTSファイルを操作するので、LLしないとTypeScriptをJavaScriptをコンパイルする際に
ロードにすごく時間がかかり、UXが悪くなる。

hoge.module.tsを用意することで、実行が必要なときに読み込ませることができる。

ただし、LLを機能させるためには、二つの条件が必須
* `@component`の上に`@IonicPage`を記述すること
* `hoge.module.ts`のような、`@Component`のファイル名にmoduleをつけたモジュールファイルを用意すること

なお、IonicではLLするページは文字列として表記する必要がある。
ただし、importやapp.module.tsへの追加は不要。

```typescript

{ title: 'タスク一覧', component: 'TaskListPage' }
```

## Ionicのオリジナルタグについて

### アプリ表示タグ
`<ion-app></ion-app>`

`#content`でion-navにcontentというネーミングをしている(要素参照のため)
swipeBackEnabled...スワイプを無効化
[root]...中身に何を表示するか決めるプロパティ(ここではrootPageを指定)
`<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>`

### サイドバーの表示
[content]プロパティにcontentを指定することで、メニューが表示されている間は、
#contentがついている<ion-nav>を右に押し出して表示する。
<ion-menu [content]="content"></ion-menu>

## TypeScriptについて

pagesの型宣言(TypeScript)

  title => string型
  component => any型

```typescript

pages: Array<{title: string, component: any}>;
```

this.pagesになっているのは、pagesがメソッドとして括られているため
  これにより、サイドバーをデータバインディングしている

```typescript

this.pages = [
  { title: 'Hello Ionic', component: HelloIonicPage },
  { title: 'My First List', component: ListPage }
];
```

メソッドの定義(ここでは、(click)で発火させるためのメソッドを定義している)

```typescript

openPage(page) {
  // close the menu when clicking a link from the menu
  this.menu.close();
  // navigate to the new page if it is not the current page
  this.nav.setRoot(page.component);
}
```

## Angularについて

`*ngFor`...繰り返しを指示する構文(*ngFor="let <変数> of <展開する配列>")
`let p of pages`...配列のpagesからpという名前で順番に値を取り出す
`(click)`...onClickと同じ意味。ユーザーがクリックすると、指定したメソッド(ここではopenPage(p))が実行される。
(click)で発火するメソッドは、app.components.tsなどの各component.tsで定義する

```typescript

<button ion-item *ngFor="let p of pages" (click)="openPage(p)">
  <!--pの中のtitleを取り出す-->
  {{p.title}}
</button>
```

配列の作り方

```typescript

export class HelloIonicPage {
  // string型のtitle変数を定義し、デフォルトの値を代入する
  title: string = 'タスク登録';
  //　配列tasksを用意し、オブジェクトにstring型のnameを格納
  //  nameをキーとしたオブジェクトの型が{ name: string } でそれを配列[]で格納
  // 配列[]はArray<>でも表記可能
  tasks: { name: string }[] = [
    { name: 'タスク1' },
    { name: 'タスク2' },
  ];
  constructor() {

  }
}
```

### 双方向でデータバインディングを行う

#### テンプレート側

 `[(ngModel)]="task" name="task"`

#### TSファイル側

`task: string;`

バリデーションのやり方

1. formタグの中で`#f="ngForm`とすることで、ローカル変数を定義する
2. `required`や`minlength/maxlength`などを使ってバリデーションを定義
3. submitの箇所で`[disabled]="!if.form.valid"`とすれば、条件に満たしてない場合にボタンがdisabledになる。

```typescript

<ion-content padding>
  <!--submitイベントが発生したときにaddTask()メソッドを呼び出す-->
  <!--#f=ngFormによってHTMLテンプレート内で使うローカル変数の定義を行う-->
  <form padding (submit)="addTask()" #f="ngForm">
    <ion-list>
      <ion-item>
        <ion-label floating>タスク</ion-label>
        <ion-input type="text" [(ngModel)]="task" name="task"
                    required minlength="3" maxlength="20"></ion-input>
      </ion-item>
    </ion-list>
    <button type="submit" ion-button block [disabled]="!f.form.valid">追加</button>
  </form>

  <ion-list>
    <ion-item *ngFor="let t of tasks">{{t.name}}</ion-item>
  </ion-list>
</ion-content>
```