HTML5Experts.jp

FirebaseとAngular2を使ってリアルタイムでデータのやり取り【導入編】

Firebaseはリアルタイム同期なデータベースを中心に、Auth認証やPushNotificationやユーザーの行動分析といったアプリケーションに最適な機能を提供するBasSです。先月行われたGoogle IOにて、PushNotificationや行動分析機能が追加されるなど話題となりました。

今回はFirebaseのリアルタイムベースとAngular2を用いて、リアルタイムにデータをやり取りする方法について解説します。

Firebaseのリアルタイムデータベース

Firebaseのリアルタイムデータベースはクラウドホスト型のNoSQLデータベースです。JSONの形式でデータは保存されます。 APIはもちろん、iOS/Android/Web用のSDKが提供されているので、非常に簡単に導入することができます。

また、ネットワークが途中で切れた場合は自動的に再接続を行い、復帰後に改めてデータが同期されます。同時100コネクション、1GBのデータ、10GBの転送量までは無料で使うことができるので、まず試してみるといったことができます。

今回はこの無料プランとAngular2を使って進めていきます。

Angular CLIで環境構築

Angular2の環境構築が初めての方でも簡単に導入できるように、Angular2ではCLIが公開されています。これを利用して環境を構築していきます。(※別途、Node.jsのインストールが必要です)

angular-cli

まずはnpmを使って、angular-cliをインストールします。

npm install -g angular-cli

インストールが成功していれば、ng versionでバージョンが表示されます。

angular-cli: 1.0.0-beta.5
node: 5.6.0
os: darwin x64

続いて、ng newコマンドを用いてプロジェクトを作成します。newの後には任意のプロジェクト名を入力してください。(angularやfirebaseという名称を用いるとパッケージ名が重複してしまうため、関連ライブラリのインストールに失敗するのでご注意ください)

ng new sample
cd sample

今回はng new sampleでプロジェクトを作成します。

sampleというフォルダができるのでcd sampleで移動し、この状態で ng serve コマンドを実行するとサンプルのアプリケーションがビルドされ、http://localhost:4200にブラウザでアクセスするとsample works!と表示されます。

これで開発環境の準備が整いました。

AngularFire導入

本題のAngularFireを導入していきます。

angularfire2

npm install angularfire2 firebase@2.4.2 --save

※ 6月14日現在の最新版は、firebase 2.4.2に対応しています。

続いて、firebase用の型定義ファイルをインストールします。 typingsがインストールされていない場合は npm i -g typingsを実行してインストールをしてください。

typings install dt~firebase --save --global

※ 注意

typings 1系と0系では、コマンドやインストールされるファイルの構成が異なります。現在のバージョンのangular-cliでは0系でインストールされますが、npmでインストールされる最新版は1系になります。本稿では1系がインストールされている状態で解説を行います。0系の場合は以下のtypingsに追記する作業は必要ありません。

src/typings.d.tsにインストールされたfirebaseの型定義ファイル群を追記します。

/// <reference path="../typings/browser.d.ts" />
/// <reference path="../typings/index.d.ts" />
declare var module: { id: string };

angular-cli-build.jsを開き、vendorNpmFiles'angularfire2/**/*.js' 'firebase/lib/*.js' を追記します。

var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) { return new Angular2App(defaults, { vendorNpmFiles: [ 'systemjs/dist/system-polyfills.js', 'systemjs/dist/system.src.js', 'zone.js/dist//.+(js|js.map)', 'es6-shim/es6-shim.js', 'reflect-metadata/*/*.+(js|js.map)', 'rxjs//.+(js|js.map)', '@angular/*/.+(js|js.map)', 'angularfire2/*/.js', // <- 追加 'firebase/lib/.js' // <- 追加 ] }); };

続いて、/src/system-config.tsに以下のようにfirebase,angularfire2の記述を追記します。

/** Map relative paths to URLs. */
const map: any = {
  'firebase': 'vendor/firebase/lib/firebase-web.js',
  'angularfire2': 'vendor/angularfire2'
};

/** User packages configuration. */ const packages: any = { angularfire2: { defaultExtension: 'js', main: 'angularfire2.js' } }

これでAngular2でFirebaseを使う準備が整いました。

Firebaseの登録

続いて、Firebaseの登録と設定を行っていきましょう。

firebase

FirebaseはGoogleアカウントで登録することができます。登録が完了すると、プロジェクトを作るためのボタンが表示されます。

「新規プロジェクトを作成」ボタンをクリックして、アプリケーションを作成しましょう。

アプリケーションが作成できたら「Database」ページを開きます。 以下のように、まだデータベースには何も入っていません。まずはこの画面から初期データを登録してみましょう。

このようにデータを作成しました。

{
  items: {
    0: {
      value: 'item0001'
    },
    2: {
      value: 'item0002'
    },
    2: {
      value: 'item0003'
    }
  }
}

Firebase上では、すべてオブジェクトの形式でデータが構成されるようにしておきます。

続いて、「ルール」のタブをクリックしてルールを設定します。 ルールはデータベースへの値に対してのアクセス権の設定を行うことができます。JSONの形式でデータを定義していきます。

詳しいルールの記載は、以下のページに説明があります。 https://html5exp-sample.firebaseio.com/

ここではサンプルということで、読み書きともに誰からでもできるように設定しておきましょう。

{
  "rules": {
    "items": {
      ".read": "true",
      ".write": "true"
    }
  }
}

これによってデータベースのitems以下は、読み書きともに誰でも許可されるようになりました。では、これで登録したデータが読み取れるか試してみましょう。/src/main.tsを開きます。

import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { SampleAppComponent, environment } from './app/';
import { FIREBASE_PROVIDERS, defaultFirebase } from 'angularfire2'; // <- add

if (environment.production) { enableProdMode(); }

bootstrap(SampleAppComponent,[ FIREBASE_PROVIDERS, // <- add defaultFirebase('https://<your-firebase-app>.firebaseio.com') // <- add ]);

main.ts にangularfire2のファイルをimportし、bootstrap関数の第2引数にFIREBASE_PROVIDERSdefaultFirebase('https://.firebaseio.com') を配列で渡しておきます。

こうすることによって、今後Angular2のコンポーネント内でFirebaseに必要な設定とプロバイダが提供されるようになります。

続いて、画面にデータを表示するためのコンポーネントに変更を加えます。

import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2'; //<- add

@Component({ moduleId: module.id, selector: 'sample-app', templateUrl: 'sample.component.html', styleUrls: ['sample.component.css'] }) export class SampleAppComponent { title = 'sample works!'; items: FirebaseListObservable<any[]>; constructor(af: AngularFire) { this.items = af.database.list('/items'); } }

<h1>{{title}}</h1>
<ul *ngFor="let item of items | async">
  <li class="text">
    {{item.value}}
  </li>
</ul>

*ngFor="let item of items | async" のasyncはAsyncPipeと呼ばれるAngular2にはじめから組み込まれているPipeのひとつです。これはitemsのObservableをsubscribeしたときの引数を自動的に取得して表示してくれるPipeです。詳しい使い方や解説は以下のページにありますので、こちらをご一読ください。

Async Pipe

ng serve コマンドを改めて実行すると、Firebaseで登録されたデータが表示されるようになります。

データの追加

次にブラウザからデータを追加する処理を実装していきましょう。

export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable<any[]>;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
}

<h1>{{title}}</h1>
<ul *ngFor="let item of items | async">
  <li class="text">
    {{item.value}}
  </li>
</ul>

<form (ngSubmit)="addItem(newItem.value);newItem.value=''"> <input type="text" #newItem> <button>send</button> </form>

FirebaseListObservableにデータをpushするだけで配列データにデータを追加することができます。

データの更新

今度はリストのデータを更新してみましょう。

export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable<any[]>;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
  updateItem(key:string,value:string) {
      this.items.update(key,{value:value});
  }
}

<h1>{{title}}</h1>
<ul *ngFor="let item of items | async">
  <li class="text">
    {{item.value}}
    <input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/>
  </li>
</ul>

<form (ngSubmit)="addItem(newItem.value);newItem.value=''"> <input type="text" #newItem> <button>send</button> </form>

this.items.update(key,{value:value});のように更新したいデータの$keyと値を渡すことで更新ができます。 Firebaseのオブジェクトには$keyというプロパティがあり、各データのKey名を取得することができます。 pushで追加されたデータは、一意なKeyが自動で振られるようになっています。

データの削除

続いて、リストのデータを削除してみましょう。

export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable<any[]>;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
  updateItem(key:string,value:string) {
      this.items.update(key,{value:value});
  }
  deleteItem(key:string) {
      this.items.remove(key);
  }
  deleteAll() {
      this.items.remove();
  }
}

<h1>{{title}}</h1>
<ul *ngFor="let item of items | async">
  <li class="text">
    {{item.value}}
    <input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/>

&lt;button (click)="removeItem(item.$key)"&gt;削除&lt;/button&gt;

</li> </ul>

<form (ngSubmit)="addItem(newItem.value);newItem.value=''"> <input type="text" #newItem> <button>send</button> </form>

<button (click)="removeAll()">全削除</button>

更新と同様にthis.items.remove(key);と削除したいデータのKeyを渡すことで、そのデータのみを削除することができます。

this.items.remove();とKeyを渡さずにremoveするとリスト全体が削除されるので注意してください。

このように非常に簡単にデータの読み書きが実装できてしまいます。

オブジェクトデータの取り扱い

続いて、今度はリストデータでなく単一のオブジェクトのデータを扱ってみましょう。Firebaseの管理画面でDatabaseのデータに以下のようなconstantsを追加しました。

{
  constants: {
    title: "My first firebase app"
  },
  items: {
    ...
  }
}

ルール設定ページにいき、constantsは読み込み専用に設定しておきます。

{
  "rules": {
    "constants": {
      ".read": "true",
      ".write": "false"
    },
    "items": {
      ".read": "true",
      ".write": "true"
    }
  }
}

src/app/sample.component.tsを、以下のように修正します。

import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2'; //<- add

@Component({ moduleId: module.id, selector: 'sample-app', templateUrl: 'sample.component.html', styleUrls: ['sample.component.css'] }) export class SampleAppComponent { constants: FirebaseObjectObservable<any>; items: FirebaseListObservable<any[]>; constructor(af: AngularFire) { this.constants = af.database.object('/constants'); this.items = af.database.list('/items'); } addItem(value:string) { this.items.push({value:value}); } updateItem(key:string,value:string) { this.items.update(key,{value:value}); } deleteItem(key:string) { this.items.remove(key); } deleteAll() { this.items.remove(); } }

そして、ビューのタイトル部分をconstantsから取得して表示させるようにします。

<h1>{{(constants | async)?.title}}</h1>
<ul *ngFor="let item of items | async">
  <li class="text">
    <input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/>

&lt;button (click)="deleteItem(item.$key)"&gt;削除&lt;/button&gt;

</li> </ul>

<form (ngSubmit)="addItem(newItem.value);newItem.value=''"> <input type="text" #newItem> <button>send</button> </form>

<button (click)="deleteAll()">全削除</button>

こうすることで、オブジェクトのデータを読み込んで画面に表示させることができました。

オブジェクトの更新

オブジェクトの更新をする際は、リストと同様にupdate関数を用いて変更を行うことができます。その時にはルールで書き込みが許可されている必要があります。

this.constants.update({title:"new title"});

オブジェクトの削除

オブジェクトの削除を行う際にはremove関数を用いますが、以下のようにremoveするとconstantsごと削除されます。

this.constants.remove(); // constantsごと削除

1つのプロパティのみを削除したい場合は、以下のように削除したいプロパティまで取得した上でremove関数を実行します。

af.database.object('/constants/title').remove(); //titleだけ削除

まとめ

このようにAngular2とFirebaseを使うことで、簡単にリアルタイムにデータのやり取りを行うことができるようになります。

今回はサンプルということで誰でも読み書きできる形にしていますが、ユーザー認証をFirebaseで作ったり、既にあるログイン情報を利用して認証させたりできるので、新規でも追加でも導入しやすくなっています。

次回はその認証機能を用いてログインすると書き込みができるようにしていきたいと思います。