electron-vueとNeDBでデスクトップアプリ作成



先日私事でelectron-vueとNeDBでデスクトップアプリを作成したので、忘れないうちにメモ。
途中はまった箇所も何箇所か・・

- vue-routerの設定
- bootstrap4のscss設定
- jQueryの設定
- NeDBのlike検索
- NeDBのdb配置場所

electronもvue.jsも初めてなので、何か間違いとかこうやったほうが良いとかご指摘ありましたらお願いします。


# 作成するもの
簡易データベース検索アプリ

# 使用するフレームワーク
- [electron-vue](https://github.com/SimulatedGREG/electron-vue)
- [NeDB](https://github.com/louischatriot/nedb)
- [bootstrap4](https://v4-alpha.getbootstrap.com/)

# 作業環境構築
## electron-vueのインストール

```
// グローバルにvue-cliをインストール
$ npm i -g vue-cli

// electron-vueの設定
$ vue init simulatedgreg/electron-vue my-project

```

色々設定が出てきますが、今回はEslintのみNoでその他はEnterで行きます。

![vue-cli-install.gif](https://qiita-image-store.s3.amazonaws.com/0/114396/bb480227-fe2e-87e6-2967-8abc3080420e.gif "vue-cli-install.gif")

その後パッケージをインストールし、デモを起動

```
$ npm i
$ npm run dev
```

この無事画面が出たらインストールOKです。


![スクリーンショット 2017-05-18 17.16.21.png](https://qiita-image-store.s3.amazonaws.com/0/114396/51d7437b-3efa-6f7d-8710-42189b2a8c56.png "スクリーンショット 2017-05-18 17.16.21.png")

## topページの追加とルーティング
トップがデモ画面のままだと何かとやりにくいので、トップページを新たに追加します。

app/src/renderer/componentsにファイルindex.vueを作成して以下を記述

```html:app/src/renderer/compontents/index.vue
<template>
    <div>
        <div class="main">
            <h1>Hello electron-vue. My name is {{ name }}</h1>
        </div>
    </div>
</template>

<script>
    export default {
        data () {
            return {
                name: 'hoge'
            }
        },
        name: 'index'
    }
</script>

<style>
html, body, main {
    height: 100%;
}
main {
    display: flex;
    justify-content: center;
    align-items: center;
}
</style>
```

そしてそれをレンダリングできるようにrouterの設定を行います。
~~編集するファイルはapp/src/renderer/routes.jsです。~~
[追記]
routingを行うファイルのpathが変更になったようです。詳細はコメントをみてください。

一旦すべて削除し、以下を記述します。
ここでかなりつまづいたのですが、[こちら](http://qiita.com/nrk_baby/items/9d2d0f186437133d01d6)の投稿のおかげで解決できました。
ポイントは,path: '*',の後にルートを追加しても反映されないので、必ずリンクはその前に記述するということです。

```js:app/src/renderer/routes.js
export default [
  {
    path: '/',
    name: 'index',
    component: require('components/index')
  },
  {
    path: '*',
    redirect: '/'
  }
]

```

こんな画面が出れば成功です。


![スクリーンショット 2017-05-18 17.42.45.png](https://qiita-image-store.s3.amazonaws.com/0/114396/e830eab5-5168-04b6-7d0f-7e063bfe9403.png "スクリーンショット 2017-05-18 17.42.45.png")





## bootstrapのインストール

次にbootstrapのインストールとjqueryの設定、scssの設定を行います。

### bootstrap.jsのimport、jqueryの設定
今回はscssを使いたいのでbootstrap4をインストールします。
bootstrap等のプラグインは/app内でインストールするので注意！

```
// appに移動
$ cd app
// bootstrap4の追加
$ npm install bootstrap@4.0.0-alpha.6
```

これをmain.jsで読み込みます。
app/src/renderer/main.jsに以下を追加

```js:main.js
.
.
// bootstrapの読み込み
require('bootstrap')

```

でOKかと思いきや、以下エラーが発生して画面が真っ白に。

```
Uncaught Error: Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.
```

bootstrapにはjqueryが必要でしたね。これを設定します。
jquery自体はbootstrapをnpmでinstallした際に追加されてるはずなので、webpackにjqueryを読み込む記述を追加します。

ルートディレクトリのwebpack.renderer.config.jsを以下のように編集。

```js:webpack.renderer.config.js

plugins: [
    new ExtractTextPlugin('styles.css'),
    new HtmlWebpackPlugin({
        filename: 'index.html',
        template: './app/index.ejs',
        appModules: process.env.NODE_ENV !== 'production'
            ? path.resolve(__dirname, 'app/node_modules')
            : false
    }),
    new webpack.NoEmitOnErrorsPlugin(),
    // 追加 ↓
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        "Tether": 'tether',
    }),
    // ここまで
],

```

これでエラーが消えるはずです。

### bootstrap.scssの設定

今回は後ほど編集しやすいように、main.scssを新たに作り、そこにbootstrap.scssをインポートする形で設定します。

```
// rendererに移動
$ cd app/src/renderer
// main.scssの作成
$ touch main.scss
```

main.scssに以下を記述

```scss:main.scss

// bootstrap読み込み
@import '../../node_modules/bootstrap/scss/bootstrap.scss';


h1 {
    color: $brand-primary;
}

```

main.jsに以下を追加して読み込みます。

```js:main.js
// styleの読み込み
import './main.scss'
```

webpackにscssのloaderがないためエラーが出るので、
scss-loaderをダウンロードし、さらにwebpack.renderer.config.jsにscssの設定をします。

sass-loaderとnode-sassのインストール
※ このインストールはwebpackの設定があるルートディレクトリ(my-project)で行うので注意！

```
//ルートディレクトリに移動してから・・
$ npm i sass-loader node-sass
```

wepackの設定追加

```js:webpack.renderer.config.js
  {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
      fallback: 'style-loader',
      use: 'css-loader'
    })
  },
  //以下を追記
  {
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
      fallback: 'style-loader',
      use: 'css-loader!sass-loader'
    })
  },
  // ここまで
  {
    test: /\.html$/,
    use: 'vue-html-loader'
  },
  
```

これで起動してみて以下のように文字色が変わればbootstrapのインポートはOKです。

![スクリーンショット 2017-05-18 22.20.27.png](https://qiita-image-store.s3.amazonaws.com/0/114396/ba50cb67-65de-8fc2-e67f-faf658809609.png "スクリーンショット 2017-05-18 22.20.27.png")


## NeDBの設定
electronでアプリを配布する際に便利なNoSQLデータベースのNeDBの設定を行います。

### パッケージインストール
まずはnpmでインストール。（こちらはapp/で）

```
$ npm i nedb
```

### テストデータの作成
今回はjsonを元データとして使用するので、app/src/renderer/components以下にdbフォルダを作り、namelist.jsonを作成します

```json:app/src/renderer/components/db/namelist.json
[
    {"name":"山田","from":"大阪","age":"20"},
    {"name":"田中","from":"北海道","age":"23"},
    {"name":"佐藤","from":"茨城","age":"35"},
    {"name":"鈴木","from":"沖縄","age":"22"},
    {"name":"井上","from":"福島","age":"52"},
    {"name":"村田","from":"鹿児島","age":"34"}
]
```

### データベースの作成・検索処理の実装
NeDBの読み込み。DBの設定。レコードのインサートを行います。
インメモリでDBを作成し、先ほど作ったテストデータをインサートしてます。

```index.vue
<script>
//インメモリでDB作成
var Database = require("nedb");
var namelistDB = new Database();
//データ読み込み挿入
import dbData from './db/namelist.json';
namelistDB.insert(dbData);
.
.
.
</script>
```

### like検索の実行
先ほど作ったDBからレコードを検索できるように、vue.jsとの連携部分を記述します。index.vueのスクリプトに以下を追加してください。

```index.vue
export default {
    data () {
        return {
            findWord : '',
            items: '',
        }
    },
    methods: {
        dbQuery: function () {
            namelistDB.find({name: new RegExp(".*"+this.findWord+".*", "i")} , (err, doc) => {
                this.items = doc;
            };
        }
    },
    name: 'index'
}

```

ここでのポイントはmethodsのdbQueryの記述です。
RegExp()スクリプトを使うことでLike検索を実現しています。

```
namelistDB.find({name: /findWord/} ...
```

上記では変数(findWord）が展開されず、適切な検索ができないので注意してください。
その他、NeDBのインサート、検索については [こちら](http://qiita.com/tinymouse/items/0731eef4aebf2779bd0b) が大変参考になります。


`<template>` 部分の記述も含め最終的なコードは下記の通りとなります。

```html:index.vue
<template>
    <div id="app">
        <div class="container">
            <p class="text-center">名前を入力してください</p>
            <div class="form-group">
                <input type="text" v-model="findWord" class="form-control form-control-lg">
                <button @click="dbQuery" class="btn btn-success btn-lg">検索</button>
            </div>
            <!-- 検索結果テーブル -->
            <table class="table table-striped table-bordered mg-t-50 search-table" v-if="items != ''">
                <thead>
                    <tr class="table-success">
                        <td>名前</td>
                        <td>出身</td>
                        <td>年齢</td>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="item in items">
                        <td>{{ item.name }}</td>
                        <td>{{ item.from }}</td>
                        <td>{{ item.age }}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
//インメモリでDB作成
var Database = require("nedb");
var namelistDB = new Database();
//データ読み込み挿入
import dbData from './db/namelist.json';
namelistDB.insert(dbData);

export default {
    data () {
        return {
            findWord : '',
            items: '',
        }
    },
    methods: {
        dbQuery: function () {
            namelistDB.find({name: new RegExp(".*"+this.findWord+".*", "i")} , function (err, doc) {
                this.items = doc;
            }.bind(this));
        }
    },
    name: 'index'
}
</script>

```

これでこのような画面が表示されるはずです。
![5月-18-2017 23-55-09.gif](https://qiita-image-store.s3.amazonaws.com/0/114396/f4c082d2-259b-abaf-d95f-432112c6b817.gif "5月-18-2017 23-55-09.gif")