angularfire2を使ってTOUR OF HEROESをいじってみた 

[Angular Advent Calendar](http://qiita.com/advent-calendar/2016/angular) の11日目用のポストです。

リポジトリ [https://github.com/can-i-do-web/angularfire2-tour-of-heroes](https://github.com/can-i-do-web/angularfire2-tour-of-heroes)

## angularfire2 とは
[angular/angularfire2](https://github.com/angular/angularfire2/) にあるとおり、Angular 公認のライブラリで [firebase](https://firebase.google.com/?hl=ja) とのやり取りを簡単にやってくれる素敵なヤツです。

これを使ってみたくて、 Angular のチュートリアルである TOUR OF HEROES (以下TOH) に firebase へのCRUD機能をつけてみました。 

## まずは TOS をつくる
これはチュートリアル見ながら、ちまちま作っていけばOKです。
面倒な人は、[完成したのがある](https://angular.io/resources/live-examples/toh-5/ts/eplnkr.html)のでコピペすると良いです（途中で知った...orz）

TOS では `SystemJS` ベースですが、今回は `angular-cli` ( `webpack` )ベースで実装しています。そのため `@Component` に `moduleId: module.id` を記述すると動かなくなったりと軽く罠を踏みます。お気を付けくださいませ。

TOS は [6.ROUTING](https://angular.io/docs/ts/latest/tutorial/toh-pt5.html) までを実装して、ここに firebase の機能を足していきます。

## angularfire2 の設定をする
基本的には [angular/angularfire2](https://github.com/angular/angularfire2/) に使い方があるのでそれを参考にしながら進めます。

1. [firebase](https://firebase.google.com/?hl=ja) のサイト上でアカウントやらDBやらの設定をします。TOSで利用していた mock をJSON形式にして、登録しておきます。
1. firebase angularfire2 をインストールする

    ```
    npm install firebase angularfire2 --save
    ```

1. 依存モジュールをインストールする
 
    ```
    npm install -g typings 
    npm install -g typescript
    ```

1. angularfire2 を import する

    ```js:src\app\app.module.ts
    import { NgModule }       from '@angular/core';
    import { BrowserModule }  from '@angular/platform-browser';
    import { FormsModule }    from '@angular/forms';

    import { AngularFireModule } from 'angularfire2';

    import { AppComponent }         from './app.component';
    import { DashboardComponent }   from './dashboard.component';
    import { HeroDetailComponent }  from './hero-detail.component';
    import { HeroesComponent }      from './heroes.component';
    import { HeroService }          from './hero.service';

    import { AppRoutingModule }     from './app-routing.module';
    import { FIREBASE_CONFIG } from './app-firebase.config';


    @NgModule({
      imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule,
        AngularFireModule.initializeApp(FIREBASE_CONFIG)
      ],
      declarations: [
        AppComponent,
        DashboardComponent,
        HeroDetailComponent,
        HeroesComponent
      ],
      providers: [ HeroService ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    ```

    `FIREBASE_CONFIG` は firebaseのサイトから確認して以下のように保存しておきます。

    ```src\app\app-firebase.config.ts
    export const FIREBASE_CONFIG = {
      apiKey: '<your-key>',
      authDomain: '<your-project-authdomain>',
      databaseURL: '<your-database-URL>',
      storageBucket: '<your-storage-bucket>'
    };
    ```

    これで設定はおわりで、あとは各ページで CRUD の処理を足していきます。

1. たぶん動かない
    後になって発覚したのですが、[Installation and Setup](https://github.com/angular/angularfire2/blob/master/docs/1-install-and-setup.md#troubleshooting) の最後に Troubleshooting ってあってこの辺の対応が必要になるかもしれません。
ボクはさらに、「[この型が被ってるぞ！](https://github.com/angular/angularfire2/issues/609)」っていうエラー/ワーニングに出くわしたため、いろいろ試した結果、step2の

    ```
    npm install angularfire2 --save
    ```

    として、`firebase` のインストールをしていません。
    `angularfire2` の依存モジュールにも `firebase` があって、どうやらかち合ってしまうようです。
    あと、違うバージョンでインストールされるし、動いてた時期もあったしで、もうよくわかりません...。現状動いてるので良しとしました。

## CRUD

`angularfire2` のAPIをたたいてるのは、以下です。

```js:src\app\hero.service.ts
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import { Promise } from 'firebase';

import { Hero } from './hero';
import { Injectable } from '@angular/core';

@Injectable()
export class HeroService {
  heroes: FirebaseListObservable<Hero[]>;

  constructor(private af: AngularFire){
    this.heroes = this.af.database.list('/heroes');
  }

  getHeroes(): FirebaseListObservable<Hero[]> {
    return this.heroes;
  }

  getHero(key: string): FirebaseObjectObservable<Hero> {
    return this.af.database.object(`/heroes/${key}`);
  }

  updateHero(hero: Hero): Promise<void> {
    return this.heroes.update(hero.$key, { name: hero.name });
  }

  addHero(newHero: string): Promise<void> {
    return this.heroes.push({
      id: 100, name: newHero
    });
  }

  removeHero(hero: Hero): Promise<void> {
    return this.heroes.remove(hero.$key);
  }
}
```

`constructor` で firebase 上の `heroes` というテーブルに接続します。
ここでリターンされる `FirebaseListObservable` に対して所定のCRUDの操作をすると、firebaseと同期してくれます。`FirebaseObjectObservable` というのもあって、これはリストに対して( `FirebaseListObservable` ) ではなく、個別のデータに対して同様の操作を提供してくれます。
注意すべきは各データに `$key` とか `$value` とかプロパティを勝手に追加してくるので型定義ファイルに追記する必要がありました。

出来上がりの図( update )
![update.gif](https://qiita-image-store.s3.amazonaws.com/0/17439/f5d2fc7d-6f1d-afee-275c-3a7ef4341221.gif)

出来上がりの図( add, delete )
![add-del.gif](https://qiita-image-store.s3.amazonaws.com/0/17439/86e95851-7dcb-2275-acac-b924ccea5016.gif)


データを `JSON` インポートで投入すると `$key` がインデックスになるけど、アプリから `add` すると Hash 値になるのはそういうものという認識であってる？詳しい人いたら教えてほしいです。

## まとめ
構築にはまったものの、使えるようになってしまえば実装は簡単でした。全容を把握できていないものの趣味で使うには申し分なさそうです。firebase は認証機能とかもあるので、その辺もあわせてまた触ってみよう。