Angularfire v7への更新

Angular

TypeScript

Firestore

Firebase

はじめに

今回の記事はAngularfireのv7を使用してみたので、その実装例を備忘録的に書いていきます。

また、今回の記事は以下を参考にしております。

開発環境

  • yarn 1.22.17
  • Angular 12
  • Angularfire 7.2.0

準備

前提としてfirebaseのプロジェクトは作成済みとしておきます。

その上でAngularのプロジェクト作成を行い、Angularfireの追加を行っていきます。

-> % yarn ng new v7 yarn run v1.22.17 $ /<your project>/node_modules/.bin/ng new v7 ? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? SCSS [ https://sass-lang.com/documentation/syntax#scss ] ... ✔ Packages installed successfully. Directory is already under version control. Skipping initialization of git. ✨ Done in xx.xxs. -> % cd v7 -> % yarn build

プロジェクトが作れたので、次はAngularfireを追加していきます。

-> % yarn ng add @angular/fire yarn run v1.22.17 $ ng add @angular/fire ℹ Using package manager: yarn ✔ Found compatible package version: @angular/fire@7.2.0. ✔ Package information loaded. The package @angular/fire@7.2.0 will be installed and executed. Would you like to proceed? Yes ✔ Package successfully installed. UPDATE package.json (1145 bytes) ✔ Packages installed successfully. ? What features would you like to setup? Firestore Using firebase-tools version 9.21.0 ? Which Firebase account would you like to use? <your account> ✔ Preparing the list of your Firebase projects ? Please select a project: <your created app name> ? Please select a hosting site: <your site url> ✔ Preparing the list of your Firebase WEB apps ? Please select an app: v7 ✔ Downloading configuration data of your Firebase WEB app CREATE .firebaserc (185 bytes) UPDATE .gitignore (658 bytes) UPDATE src/app/app.module.ts (706 bytes) UPDATE src/environments/environment.ts (996 bytes) UPDATE src/environments/environment.prod.ts (389 bytes) UPDATE angular.json (3576 bytes) UPDATE firebase.json (777 bytes) ✨ Done in 139.30s.

buildを行ってから実行するとappの選択時に今作っているプロジェクトを選択できるっぽい?ので先にbuildを回してからライブラリの追加を行っています。

今まではenviroment.tsなどに自分で追加しなければいけなかったAPIキーなどは自動で追加されるようになっているようですので、firebase上でのプロジェクトもあらかじめ作成しておくと便利です。

実装

まずは使用するモジュールの追加をしていきましょう。 ...と言いたいところなのですが、これらも自動で追加されるようになったようなので確認していきます。

// app.module.ts ... import { initializeApp,provideFirebaseApp } from '@angular/fire/app'; import { environment } from '../environments/environment'; import { provideFirestore,getFirestore } from '@angular/fire/firestore'; @NgModule({ ... imports: [ ... provideFirebaseApp(() => initializeApp(environment.firebase)), provideFirestore(() => getFirestore()) ], ... }) export class AppModule { }

関係のない部分は省略していますが、上記のようにモジュールが追加されているかと思います。 firebase emulatorsのポート設定などもapp.module.tsで追加可能になったようなので気になる方は是非調べてみてください。

データの送信

では早速データの送信を行っていきます。 今回は簡単に実装したいので、app.component.htmlを以下のように修正しました。

<div class="container"> <button (click)="post()">送信</button> </div>

これで見た目の作成ができたので、実装に移ります。 まずは送信するデータ型が欲しいのでこれもapp.component.tsに追加していきます。

// app.component.ts import { Timestamp } from '@firebase/firestore'; interface Postcard { createdAt: Timestamp title: string content: string }

本来であれば別ファイルで管理しますが、サンプルなのでこれでOKです。 あとは、post()を作成していきます。

// app.component.ts import { Component } from '@angular/core'; import { collection, doc, Firestore, setDoc } from '@angular/fire/firestore'; import { CollectionReference, Timestamp } from '@firebase/firestore'; interface Postcard { createdAt: Timestamp title: string content: string } @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'v7'; postcard: Postcard = { createdAt: Timestamp.now(), title: '', content: '' } constructor( private firestore: Firestore ){} post() { this.postcard.title = 'sample' this.postcard.content = 'hogehoge' setDoc<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' // ① ) as CollectionReference<Postcard> ), this.postcard // ② ).catch(() => { window.alert('送信に失敗しました'); }); } }

①はデータベースのドキュメント名になります。 もし、Firestore上にデータが存在していない場合はここで設定した名前が登録されます。

②は送信したいデータを渡します。

では早速アプリケーションを動かして動作確認をしていきます。

-> % yarn start

http://localhost:4200/ にアクセスすると送信ボタンがあると思うのでクリックすると、Firestoreにデータが追加されていることが確認できると思います。

データの取得

続いてデータの取得を行いっていきます。

1件取得

// app.component.ts fetch() { const docId = '<document ID>'; // ① docData<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' ) as CollectionReference<Postcard>, docId ) ).subscribe( (res) => { this.postcard = res; // ② }, () => { console.error('データ取得に失敗しました'); } ); }
<!-- app.component.html --> <button (click)="fetch()">受信</button> <div> <h1>{{postcard.title}}</h1> <div>{{postcard.content}}</div> <!-- ③ --> <div>{{postcard.createdAt.toDate() | date}}</div> </div>

①ではFirebaeのUIのからDocumentのIDをコピーして置換してください。

②では型推論ができているはずなので、キャストなどはせずに代入が可能かと思います。

③では日付をフォーマットするためにdateというパイプを使用しています。

これで受信ボタンをクリックすればデータの取得が実行されブラウザ上にデータが反映されると思います。

複数件取得

続いてデータ一覧を取得していきます。

// app.component.ts interface PostcardItem extends Postcard { id: string } ... postcards: PostcardItem[] = [] collect() { collectionData<PostcardItem>( query<PostcardItem>( collection( this.firestore, 'postcard' ) as CollectionReference<PostcardItem>, orderBy('createdAt') // ① ), { idField: 'id' } // ② ).subscribe((res) => { this.postcards = res; }); }
<!-- app.component.html --> <ul> <li *ngFor="let card of postcards"> <h1>{{card.title}}</h1> <div>{{card.id}}</div> <div>{{card.content}}</div> <div>{{card.createdAt.toDate() | date}}</div> </li> </ul>

①では取得したい一覧のソート条件を定義しています。

②では取得したデータのIDを追加しています。これを追加しない場合はID無しで取得することができます。

更新

更新についてはpost()とほぼ同様になります。

// app.component.ts updatedPostcard: Postcard = { updatedAt: Timestamp.now(), createdAt: Timestamp.now(), title: '', content: '' } update() { const docId = '<document ID>'; this.updatedPostcard.title = 'sample' this.updatedPostcard.content = 'updated' this.updatedPostcard.updatedAt = Timestamp.now() updateDoc<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' ) as CollectionReference<Postcard>, docId ), this.updatedPostcard ) }
<!-- app.component.html --> <button (click)="update()">更新</button> <div> <h1>{{postcard.title}}</h1> <div>{{postcard.content}}</div> <div>{{postcard.updatedAt.toDate()}}</div> </div>

これで更新ボタンをクリックするとFirestoreの値が更新されるかと思います。

削除

最後に削除を実装していきます。

delete() { const docId = '<document ID>'; deleteDoc( doc( collection( this.firestore, 'postcard' ) as CollectionReference, docId ), ) }
<!-- app.component.html --> <button (click)="delete()">削除</button>

動作を確認するために、まずは一覧取得ボタンをクリックしておきます。 その後削除ボタンをクリックすると一覧に表示されているデータの数が減っていくのがわかるかと思います。

おわりに

今回はFirestoreの最新バージョンについて記事にしてみました。 これについてはまだドキュメントも不十分で、参考になる記事も少なかったので四苦八苦しましたが、なんとか動作するところまで漕ぎ着けることができました。 以前と比べるとずいぶん簡略化されているので、これから新規に作る場合には便利になりそうです!

以上、最後まで読んでいただきありがとうございました!

付録

コード全文

app.component.ts
import { Component } from '@angular/core'; import { collection, collectionData, deleteDoc, doc, docData, Firestore, orderBy, query, setDoc, updateDoc } from '@angular/fire/firestore'; import { CollectionReference, Timestamp } from '@firebase/firestore'; interface Postcard { updatedAt: Timestamp createdAt: Timestamp title: string content: string } interface PostcardItem extends Postcard { id: string } @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'v7'; postcard: Postcard = { updatedAt: Timestamp.now(), createdAt: Timestamp.now(), title: '', content: '' } updatedPostcard: Postcard = { updatedAt: Timestamp.now(), createdAt: Timestamp.now(), title: '', content: '' } postcards: PostcardItem[] = [] constructor( private firestore: Firestore ){} post() { this.postcard.title = 'sample' this.postcard.content = 'hogehoge' setDoc<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' ) as CollectionReference<Postcard> ), this.postcard ).catch(() => { window.alert('送信に失敗しました'); }); } fetch() { const docId = 'document ID'; docData<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' ) as CollectionReference<Postcard>, docId ) ).subscribe( (res) => { this.postcard = res; }, () => { console.error('データ取得に失敗しました'); } ); } collect() { collectionData<PostcardItem>( query<PostcardItem>( collection( this.firestore, 'postcard' ) as CollectionReference<PostcardItem>, orderBy('createdAt') ), { idField: 'id' } ).subscribe((res) => { this.postcards = res; }); } update() { const docId = 'document ID'; this.updatedPostcard.title = 'sample' this.updatedPostcard.content = 'updated' this.updatedPostcard.updatedAt = Timestamp.now() updateDoc<Postcard>( doc<Postcard>( collection( this.firestore, 'postcard' ) as CollectionReference<Postcard>, docId ), this.updatedPostcard ) } delete() { const docId = 'document ID'; deleteDoc( doc( collection( this.firestore, 'postcard' ) as CollectionReference, docId ), ) } }
app.component.html
<div class="container"> <button (click)="post()">送信</button> <button (click)="fetch()">受信</button> <button (click)="collect()">一覧</button> <button (click)="update()">更新</button> <button (click)="delete()">削除</button> <div> <h1>{{postcard.title}}</h1> <div>{{postcard.content}}</div> <div>{{postcard.createdAt.toDate() | date}}</div> </div> <div> <h1>{{postcard.title}}</h1> <div>{{postcard.content}}</div> <div>{{postcard.updatedAt.toDate()}}</div> </div> <ul> <li *ngFor="let card of postcards"> <h1>{{card.title}}</h1> <div>{{card.id}}</div> <div>{{card.content}}</div> <div>{{card.createdAt.toDate() | date}}</div> </li> </ul> </div>

sonio

@sonio

目次