MonacaとFirebaseの連携方法をご紹介するブログの第4回目です。今回はリアルタイムデータベースを使ってみます。
先に完成品を紹介します。Firebaseの管理画面(コンソール)とアプリ側でデータの同期を行っています。アプリ側でボタンを押すと、現在時刻でデータが登録されます。そして管理画面側でデータを削除すると、それがアプリ側に反映されます。
第1回目でストレージ機能を紹介した際は、ユーザ認証をせずにデータの登録を行いましたが、あまり好ましい実装とは言えません。Firebaseでは権限をつけてデータを保存することを推奨していますので、認証を踏まえた上で実装していきたいと思います。
そこで今回は第2回で作成した認証機能のプロジェクトにコードを追記する形で作成していきます。
HTMLの変更
データの取得に際してFirebaseに接続しますので、セキュリティポリシーを変更します。具体的には connect-src *;
を追加します。
<meta http-equiv="Content-Security-Policy" content="default-src * data:; connect-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
Vueテンプレートの変更
ログイン後のページに、[データ追加]ボタンとデータを表示するリストを追加します。
<section style="margin: 10px;">
<button @click="add">データ追加</button>
<button @click="logout">ログアウト</button>
</section>
<!-- データを表示するリストを追加 -->
<section style="margin: 10px;">
<div>リスト</div>
<ul v-for="item in times">
<li>{{ item.time }}</li>
</ul>
</section>
JavaScript側の実装
変数の初期化
JavaScript側ではまず最初に times という変数(配列)を初期化します。
// 初期データの設定
data: {
user: {
isLoggedIn: false,
mailAddress: "",
password: ""
},
times: [] // 追加
},
データの追加処理
Firebaseにデータを追加する処理はとても簡単です。
firebase.database().ref('times/')
という指定でデータを保存する名称(今回はtimes)を指定します。データの保存はpushで行います。
// イベント処理
methods: {
…省略…
// データ追加処理
add: function() {
// 登録するメッセージを作成
var d = new Date;
var message = `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`
firebase.database().ref('times/').push({
time: message
})
.catch(function(error) {
alert(error.message)
})
},
}
データの取得
Firebaseリアルタイムデータベースには、データの取得を行うためのvalueイベントがあります。このイベントは、初期データを取得するタイミングと、データに変更があった場合に実行されます。
(コンソールでデータを削除した場合にも呼ばれます)
ユーザの認証状態に応じて、イベントを有効にするonと無効にするoffを設定します。
この処理はVueの構築が終わったタイミング(created内)で行います。
// デプロイ完了時のイベント
created: function() {
// ユーザのステータスが変わったら通知
var me = this;
firebase.auth().onAuthStateChanged(function(user) {
me.user.isLoggedIn = (user !== null);
var data = firebase.database().ref('times/');
// ログイン状態であればデータ取得イベントを有効化
if (user !== null) {
// データの取得
data.on('value', function(times) {
me.times = [];
times.forEach(function(time) {
me.times.push({
key: time.key,
time: time.val().time
})
});
});
} else {
// ログアウト状態ではイベントを無効化
data.off('value');
}
});
},
全データが配列で返却されますので、変数timesを初期化して一件ずつ入れ直します。データの値はval()メソッドで取得できます。
全体のコードは以下のようになります。
var onDeviceReady = function() {
// Firebaseの初期化
var config = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DATABASE_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "MESSAGING_SENDER_ID"
};
firebase.initializeApp(config);
// Vueの処理
var vm = new Vue({
el: '#app', // マウントするDOM
// 初期データの設定
data: {
user: {
isLoggedIn: false,
mailAddress: "",
password: ""
},
times: [] // 追加
},
// デプロイ完了時のイベント
created: function() {
// ユーザのステータスが変わったら通知
var me = this;
firebase.auth().onAuthStateChanged(function(user) {
me.user.isLoggedIn = (user !== null);
var data = firebase.database().ref('times/');
// ログイン状態であればデータ取得イベントを有効化
if (user !== null) {
// データの取得
data.on('value', function(times) {
me.times = [];
times.forEach(function(time) {
me.times.push({
key: time.key,
time: time.val().time
})
});
});
} else {
// ログアウト状態ではイベントを無効化
data.off('value');
}
});
},
// テンプレート
template: `
<div>
<div class="center"> Firebase認証 </div>
<section style="margin: 10px;" v-if="user.isLoggedIn">
<p>{{ user.mailAddress }}</p>
<section style="margin: 10px;">
<button @click="add">データ追加</button>
<button @click="logout">ログアウト</button>
</section>
<!-- データを表示するリストを追加 -->
<section style="margin: 10px;">
<div>リスト</div>
<ul v-for="item in times">
<li>{{ item.time }}</li>
</ul>
</section>
</section>
<section v-else style="margin: 10px;">
<p>メールアドレス</p>
<p>
<input v-model="user.mailAddress" placeholder="メールアドレス" />
</p>
<p>パスワード</p>
<p>
<input v-model="user.password" placeholder="パスワード" type="password" />
</p>
<button @click="register">新規登録</button>
<button @click="login">ログイン</button>
</section>
</div>`,
// イベント処理
methods: {
// 登録処理
register: function() {
firebase.auth().createUserWithEmailAndPassword(this.user.mailAddress, this.user.password)
.catch(function(error) {
alert(error.message);
});
},
// ログイン処理
login: function() {
firebase.auth().signInWithEmailAndPassword(this.user.mailAddress, this.user.password)
.catch(function(error) {
alert(error.message);
});
},
// ログアウト処理
logout: function() {
firebase.auth().signOut();
},
// データ追加処理
add: function() {
// 登録するメッセージを作成
var d = new Date;
var message = `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`
firebase.database().ref('times/').push({
time: message
})
.catch(function(error) {
alert(error.message)
})
},
}
});
};
document.addEventListener(window.cordova ?"deviceready" : "DOMContentLoaded", onDeviceReady, false);
Firebaseのリアルタイムデータベースではデータの追加処理と、そのイベント通知が分かれているので実装がシンプルになります。チャットや複数端末でのデータ同期などに使うのが良さそうです。
なお、通信にはWebSocketが使われています。特に気にすることはありませんが、何らかの理由によりWebSocket接続が切れてしまっているとデータが反映されないので注意してください。
今回のコードはmoongift/monaca_firebase_realtimedbにアップロードしてあります。実装時の参考にしてください。