データを永続化する(Electron+Vue.jsで作るTodoアプリ)

Electron

はじめに

今日も、日々それっぽく仕上がっていくTodoアプリをいじっていきます。今回は、データを永続化してみたいと思います。

ディ○ゴスティーニ式ですね。

追いつけないほど新しい技術が次々出てくるプログラミングほど楽しいものは無いと思います。マニアックでヲタクな世界を敬遠せず、みんなでコーディングが楽しめたら良いですね〜

マニアックといえば、BrainF*chですね。
リンクを踏むのもためらうようなフザけた名前ですが、真面目に取り組んでみるととても奥が深いです。頭の体操にどうぞ。

データベースを何にするか?

データを永続化するための仕組みはたくさんあります。数えればキリがないです。

今回は、ローカルへの保存で十分かつユーザ管理をしない、とってもこじんまりとしたアプリです。ですので、シンプルにSQLiteやRealmあたりでもと思ったのですが、いいものを見つけました。

NeDBです。

GitHub - louischatriot/nedb: The JavaScript Database, for Node.js, nw.js, electron and the browser
The JavaScript Database, for Node.js, nw.js, electron and the browser - louischatriot/nedb

公式サイト曰く、

Embedded persistent or in memory database for Node.js, nw.js, Electron and browsers, 100% JavaScript, no binary dependency

https://github.com/louischatriot/nedb

100% JavaScriptとは、なんともElectronアプリと親和性が良さそうではないですか!

採用!😉

NeDBを使う下準備

NeDBの導入

まずはなにはともあれyarn。

yarn add nedb

ローカルシステムのファイルを読み書きできるようにする

この件はローカルファイルの読み書き・electron-vueを参考にしました。

ローカルファイルの読み書き · electron-vue

Electronからは自由にローカルシステム内のファイルを読み書きできるようです。でも、レンダリングを担うChromiunの制限によりやりたい放題はできないようです。
そりゃそうか。

この制限をクリアするために、app.getPath(name)でDBのパスを明示してやります。

import Datastore from 'nedb'
import path from 'path'
import { remote } from 'electron'
export default new Datastore({
  autoload: true,
  filename: path.join(remote.app.getPath('userData'), '/data.db')
})

importなどはsrc/renderer/main.jsに入れておまとめしまえばいいやんと思いました。
でも、あえて分けることでソースの見通しが良くなるだけでなく、別のDBを使うときに(同名にしておけば)ファイルの差し替えで済みますね。
勘違いしている気がするけど、なんとなく納得。

そして、これをsrc/renderer/main.jsで読み込んでやります。

import db from './datastore'
Vue.prototype.$db = db

2行目がミソのようです。
これにより、全てのコンポーネントファイルからthis.$dbでDBの読み書きができるようになります!!

これまで配列に出し入れしていた処理を書き換えていこう

タスクの保存

src/renderer/components/ToDo.vueに戻ってきました。

NeDBを用いてデータを書き込むときはinsertメソッドを使います。JSONデータをそのまま扱えるので、配列に保存する処理とよく似た形で書くことができます。大きく変わったところといえば、コールバックがひっついたくらいですかね。

コールバックの引数の1つ目にはエラーが入っています。問題なく書き込みができたときはnullですが、なにかしら問題が有ったときは何か入るので見てください。

2つ目は、insertされたデータが入っています。正確にはinsertに成功したデータですかね。

ちなみに、NeDBにデータをinsertすると、一意の値が割り当てられた_idフィールドが付加されます。すごく気が利いたことをして頂いて恐縮です。

insertTaskを書き換えます。

insertTask () {
    this.$db.insert({task: this.input, isComplete: false}, (error, newDoc) => {
        if (error !== null) {
        }
    })
}

ね、簡単でしょ😏

タスクの読み込み

新しいメソッドfetchTaskを付け加えます。NeDBを用いてデータを読み込むfindメソッドは、先に出てきたinsertメソッドとよく似た形をしています。

1つ目の引数には検索条件を入れます。
今回はDB内のデータ全件を取得するため{}としています。何か条件を与えるときは、JSON形式でKeyとValueを与えます。例えば{ name: Tama }とすると、nameフィールドがTamaのデータが取り出せます。

検索条件はいろいろあるので調べてみてください。

NeDB を使ってみた - Qiita
#はじめにElectron を使ってデスクトップアプリを作成しようとしています。ある程度のデータを保存しておいて検索したりしたいので、データベースエンジンを使用したいと思います。ただし今回は、ア…

コールバックの引数1つ目はおなじみですね。エラー情報が入っています。

肝心なのは2つ目のdocsです。DBを検索しえられたデータが入っています。
言わずもがなJSON形式。条件を与えたときに何も得られなかったときは空が返ってきます。

この返ってきたデータをthis.taskListに放り込んでやりましょう。

fetchTask () {
    this.$db.find({}, (error, docs) => {
        if (error !== null) {
        }
        this.taskList = docs
    })
}

タスクの読み込み

ここまでで、単純に保存することと、単純に全件取得することができました。

今度は保存したあとに読み込み処理を加えてやりましょう。先程のinsertTaskメソッドの末尾でDBの全件読み込みを行います。

insertTask () {
    this.$db.insert({task: this.input, isComplete: false}, (error, newDoc) => {
        if (error !== null) {
        }
        this.fetchTask()
    })
}

いざ実行!

追加して表示できています(ちょっと判りにくい)

うまくいきました!

タスクの初期表示

読み書きができました。めでたい!
……なのですが、アプリを再度起動してみると、登録したはずのタスクが表示されていません。

それもそのはず。
どこにもアプリ起動時にDBを読み込む処理が書かれていないからです。他の言語と同様、プログラムが気を利かせて「読み込んでおきましたよ」とはしてくれません。アプリ起動時にDBを読み込むようにしましょう。

ここで重要になるのが、アプリのライフサイクルです。

勉強しました。

Vueのライフサイクルを完全に理解した - Qiita
Vueのライフサイクルについて今回はVueのライフサイクルについて説明したいと思います。Vueのライフサイクルは以下のようになっております…

「必要なことを必要なときに」がモットーなので、今必要なところだけを調べます。

View関連(element)の呼び出しと生成処理が行われる前に、DBを読み込んでデータを用意しておかないといけません。また、コンポーネント自体のインスタンスが生成されてDBから読み込んだデータを保持する変数が存在しないといけません。

よって、インスタンスの生成が終わった後に実行されるcreatedで読み込めば良いことになりますね。

サクッと行きましょう。

data () {
    return {
        taskList: [],
        input: '',
    }
},
created () {
    this.$db.find({}, (error, docs) => {
        if (error !== null) {
        }
        this.taskList = docs
    })
},
methods: {

なお、この段階ではfetchTaskメソッドは呼び出せません。2度同じことを書くのは抵抗がありますが、致し方ないでしょう。

これでアプリ起動時にタスクが表示されるようになりました。

これも判りにくいですが、追加した後アプリを起動し直すとちゃんと表示されています

今日はここまで!

ところで、ここまで来てふと気が付きました。データが増え続けたり間違えて登録したときの削除処理はどうした?

次回はタスクが完了したときの処理と削除処理を考えていきます。

そういえばテストも書いてないや…