Angular + Cloud Firestoreでデータ登録する方法

2017年10月にリリースされたCloud Firestore(ベータ版)をAngularで利用してみました。


Cloud Firestoreとは?


Google の柔軟でスケーラブルな NoSQL クラウド データベースを使用して、クライアント側開発とサーバー側開発のデータを保存、同期します。

引用元:Cloud Firestore | Firebase


主な特徴は以下のとおりです。


  • 強力なクエリを備えたドキュメントとコレクション

  • オフライン データアクセスに対応した iOS、Android とウェブ SDK

  • リアルタイム データ同期

  • 高い整合性を持った自動マルチリージョン データ複製

  • Node、Python、Go、Java のサーバー SDK

従来のRealtime Databaseと比べ役割が似通ってますが、複雑なデータ構造が扱いやすくなる、クエリ機能、スケーラビリティなど様々な面で強化されています。

データベースを選択: Cloud Firestore または Realtime Database  |  Firebase


Angularに実装してみる

Cloud Firestoreにユーザ情報を登録するserviceクラスを実装しました。


実行環境


  • Angular: 6.0.3

  • angularfire2: 5.0.0-rc.10

  • firebase: 5.0.4


プログラム

import { Injectable } from '@angular/core';

import { Router } from '@angular/router';
import { AngularFireAuth } from 'angularfire2/auth';
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
import { User } from '../../class/user';

@Injectable({
providedIn: 'root'
})
export class UserService {

private userCollection: AngularFirestoreCollection;

constructor(
private db: AngularFirestore,
private router: Router
) {
this.userCollection = db.collection('users');
}

save(user): void {
this.db.collection('users').doc(user.uid)
.snapshotChanges()
.subscribe(data => {
if (!data.payload.exists) {
this.userCollection.doc(user.uid).set(user.getData());
}
this.router.navigate(['/']);
});
}
}


Realtime Databaseから移行しハマるとこ

キモはこの行のset関数です。

this.userCollection.doc(user.uid).set(user.getData());

公式のサンプルでは下記の様にItemクラスをそのままセットしてますが、このままではエラーになります。【2018年6月現在】

https://github.com/angular/angularfire2/blob/master/docs/firestore/collections.md

※add関数と前述のset関数の違いについては後ほど説明します。どちらもエラーになることには変わりありません。

this.itemsCollection.add(item);

試しに先程のserviceクラスを同じ様に実装して試してみます。

this.itemsCollection.doc(user.uid).set(user);

すると次のようなエラーが発生します。

FirebaseError: Function DocumentReference.set() called with invalid data. Data must be an object, but it was: a custom User object

カスタムオブジェクトはダメとのことなので、userクラスにgetData関数を追加します。

export class User {

uid: string;
displayName: string;

constructor(user) {
this.uid = user.uid;
this.displayName = user.displayName;
}

getData(): object {
const result = {};
Object.keys(this).map(key => result[key] = this[key]);
return result;
}
}

そしてset関数にはgetData関数から返された値を設定します。

this.itemsCollection.doc(user.uid).set(user.getData());

これでエラーとならず登録できるようになりました。


補足:addとsetの違い

ぼくが作成したプログラムと公式のサンプルでちょっと異なっていたsetとaddの違いですが、ドキュメントのキー(赤枠部分)を指定するかしないかの違いになります。

20180607162556.png

add関数で追加した場合はドキュメントのキーはランダム値になります。

キーを指定したい場合はdoc関数にキーを指定し、set関数にフィードの値を設定します。