Rails / Nuxt.js  / TypeScript の環境をDockerでつくる Elementの導入まで

## はじめに
[Gaiax Group Advent Calendar 2018](https://qiita.com/advent-calendar/2018/gaiax-group)の初日の記事です。

今進めているプロダクト開発でNuxt.jsを使う機会があり、環境構築をより理解するために今回Qiitaに書こうと思いました。

前提として、Ruby on Railsの環境がそこにあるとして進めていきます。


## 技術スタック
今回の環境構築での技術スタックは以下のようになります。

```
- ruby 2.5.3
- Rails 5.1.6
- node 8.1.2
- MySQL 5.7
```


## Railsのプロジェクトを作成
では環境構築を行なっていきます。

以下のコマンドで`nuxt-rails-app`という名前でアプリケーションを作成します。

```
$ rails _5.1.6_ new nuxt-rails-app --api -d mysql
```

`--api`と書いて上げることでAPIモードになります。

参考） [Rails による API 専用アプリ](https://railsguides.jp/api_app.html)

## Dockerfileを作成
次にルートディレクトリ直下にDockerfileを作成します。
作成できたらDockerfileに以下のコードを追加してください。

```
nuxt-rails-app　(ルートディレクトリ）
└── Dockerfile
```

```Dockerfile
FROM ruby:2.5.3-slim

RUN apt-get update -qq \
  && apt-get install -y build-essential default-libmysqlclient-dev \
  && rm -rf /var/lib/apt/lists/* \
  && gem update

WORKDIR /app
COPY Gemfile /app/
# COPY Gemfile.lock /app/

RUN bundle install -j4

EXPOSE  3000

CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
```

`Gemfile.lock`のところをコメントアウトしている理由は、自分のPCの環境にMySQLが入っておらず、`rails new`した際に`Gemfile.lock`ファイルが作成されていないためです。`rails new`した際に`Gemfile.lock`が作成される場合は`Gemfile.lock`のところのコメントアウトはしなくて大丈夫です。


## docker-compose.ymlを作成
次にルートディレクトリ直下にdocker-compose.ymlを作成します。
作成できたらdocker-compose.ymlに以下のコードを追加してください。

```
nuxt-rails-app (ルートディレクトリ）
└── docker-compose.yml
```

```yml
version: '2'
services:
  db:
    image: mysql:5.7.23
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: nuxt-rails-app
    volumes:
      - ./db/mysql/volumes:/var/lib/mysql
  web:
    build: .
    ports:
      - 3000:3000
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app
    links:
      - db
    env_file:
      - .env
    tty: true
    stdin_open: true
```


## .envと.env.exampleを作成
次に.envファイルと.env.exampleファイルをルートディレクトリ直下に作成します。
作成したら、.env.exampleに以下のコードを追加します。

```
USER=root
PASSWORD=
HOST=db
```


## .gitignoreを編集
次に.envファイルとdb/mysqlがコミットされないように.gitignoreファイルに以下のコードを追加します。

```
.env
db/mysql
```

## Railsのデフォルト画面を表示
以下のコマンドでビルドを実行します。

```
$ docker-compose build
```

次にコンテナを作成して、起動します。

```
$ docker-compose up -d
```

上記のコマンドを実行したら[localhost:3000](http://localhost:3000)にアクセスして、Railsのデフォルト画面が表示されていればうまく動作しています。

![image](https://i.gyazo.com/d2bba7229cc3f1a101c1e032196cd1aa.jpg)


## Dockerfileのコメントアウトを外す
DockerfileのGemfile.lockのところのコメントアウトを外します。

```Dockerfile
FROM ruby:2.5.3-slim

RUN apt-get update -qq \
  && apt-get install -y build-essential default-libmysqlclient-dev \
  && rm -rf /var/lib/apt/lists/* \
  && gem update

WORKDIR /app
COPY Gemfile /app/

# この行のコメントアウトを外す
COPY Gemfile.lock /app/ 

RUN bundle install -j4

EXPOSE  3000

CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
```


## docker-compose.ymlにfrontendコンテナの記述を追加する
次にdocker-compose.ymlにfrontendコンテナの記述を追加します。

```yml
version: '2'
services:
  db:
    image: mysql:5.7.23
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: nuxt-rails-app
    volumes:
      - ./db/mysql/volumes:/var/lib/mysql
  web:
    build: .
    ports:
      - 3000:3000
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app
    links:
      - db
    env_file:
      - .env
    tty: true
    stdin_open: true
  # ここから追加
  frontend:
    build: ./frontend
    tty: true
    ports:
      - 8000:3000
    command: yarn dev
    volumes:
      - .:/app
```


## Nuxtのプロジェクトを作成
まず以下のコマンドでvue-cliとvue/cli-initをインストールします。

```
$ npm install -g @vue/cli
$ yarn global add @vue/cli-init
```

次に以下のコマンドでNuxtのプロジェクトを作成します。

```
$ vue init nuxt-community/typescript-template frontend
```



次にfrontendディレクトリに移動して`yarn install`を実行します。

```
$ cd fronend
$ yarn install
```

## frontendディレクトリの中にDockerfileを作成
次にfrontendディレクトリ内にDockerfileを作成します。

```
frontend
└── Dockerfile
```

```Dockerfile
FROM node:8.12-slim

WORKDIR /app/frontend

ENV PATH /app/frontend/node_modules/.bin:$PATH

RUN apt-get update -qq  \
  && rm -rf /var/lib/apt/lists/*

ENV TZ Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

COPY package.json /app/frontend/package.json
COPY yarn.lock /app/frontend/yarn.lock

RUN yarn install --no-progress --registry http://registry.npmjs.org/
ENV HOST 0.0.0.0
```

## Nuxtの画面を表示
まず、以下のコマンドを実行して、コンテナを停止させます。

```
$ docker-compose down
```

次に以下のコマンドでコンテナを起動させます。

```
$ docker-compose up -d
```

上記のコマンドを実行したら、[http://localhost:8000](http://localhost:8000)にアクセスして以下の画面が表示されればうまく動作しています。

![image](https://i.gyazo.com/e53206dc15b1eb9390c8b3e3b9c438c1.png)

これで環境構築は終了ですが、Elementも導入してみます。


## Element UIも使ってみよう
Vue.js上で高機能なUIコンポーネントが使えるライブラリ「Element」は非常に便利なので、Elementの導入もここに記載したいと思います。

参考） [Element](https://element.eleme.io/#/en-US)

まず、`element-ui`をインストールします。

```
$ yarn add element-ui
```

次に`nuxt.config.js`に以下のコードを追加します。

```
frontend
└── nuxt.config.js
```

```javascript
  .
  .
  .
  // この行にelement-ui/lib/theme-chalk/index.cssを追加する
  css: ["~/assets/css/main.css", "element-ui/lib/theme-chalk/index.css"],
  build: {},
  modules: [
    "@nuxtjs/axios",
    "~/modules/typescript.js"
  ],
  // この行を追加する
  plugins: ["~plugins/element-ui"],
  axios: {}
}
```

次にfrontendディレクトリにpluginsディレクトリを作成し、作成したpluginsディレクトリにelement-ui.jsというファイルを作成します。作成したらelement-ui.jsに以下のコードを追加します。

```
frontend
└── plugins
    └── element-ui.js
```

```javascript
import Vue from "vue"
import ElementUI from "element-ui"
import locale from "element-ui/lib/locale/lang/ja"

Vue.use(ElementUI, { locale })
```

これでElementを実装できる準備が整いました。

では、Elementが使えるか試していきたいと思います。
`frontend/pages`ディレクトリにform.vueを作成し、form.vueに以下のコードを追加します。

```
frontend
└── pages
    └── form.vue
```

```vue
<template>
  <el-form ref="form" :model="form" label-width="120px">
    <el-form-item label="Activity name">
      <el-input v-model="form.name"></el-input>
    </el-form-item>
    <el-form-item label="Activity zone">
      <el-select v-model="form.region" placeholder="please select your zone">
        <el-option label="Zone one" value="shanghai"></el-option>
        <el-option label="Zone two" value="beijing"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="Activity time">
      <el-col :span="11">
        <el-date-picker
          type="date"
          placeholder="Pick a date"
          v-model="form.date1"
          style="width: 100%;"
        ></el-date-picker>
      </el-col>
      <el-col class="line" :span="2">-</el-col>
      <el-col :span="11">
        <el-time-picker
          type="fixed-time"
          placeholder="Pick a time"
          v-model="form.date2"
          style="width: 100%;"
        ></el-time-picker>
      </el-col>
    </el-form-item>
    <el-form-item label="Instant delivery">
      <el-switch v-model="form.delivery"></el-switch>
    </el-form-item>
    <el-form-item label="Activity type">
      <el-checkbox-group v-model="form.type">
        <el-checkbox label="Online activities" name="type"></el-checkbox>
        <el-checkbox label="Promotion activities" name="type"></el-checkbox>
        <el-checkbox label="Offline activities" name="type"></el-checkbox>
        <el-checkbox label="Simple brand exposure" name="type"></el-checkbox>
      </el-checkbox-group>
    </el-form-item>
    <el-form-item label="Resources">
      <el-radio-group v-model="form.resource">
        <el-radio label="Sponsor"></el-radio>
        <el-radio label="Venue"></el-radio>
      </el-radio-group>
    </el-form-item>
    <el-form-item label="Activity form">
      <el-input type="textarea" v-model="form.desc"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit">Create</el-button>
      <el-button>Cancel</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  data() {
    return {
      form: {
        name: "",
        region: "",
        date1: "",
        date2: "",
        delivery: false,
        type: [],
        resource: "",
        desc: ""
      }
    };
  },
  methods: {
    onSubmit() {
      console.log("submit!");
    }
  }
};
</script>
```

上記のコードはElementの公式ドキュメントの[こちら](https://element.eleme.io/#/en-US/component/form)をコピーしただけです。

https://element.eleme.io/#/en-US/component/form

コードを追加できたら、以下のコマンドを実行してコンテナの再起動を行います。

```
$ docker-compose down
$ docker-compose up -d
```

上記のコマンドを実行したら、[http://localhost:8000/form](http://localhost:8000/form)にアクセスします。すると下記のようにフォームのUIが表示されます。

![image](https://i.gyazo.com/93adffe5bb5d903af1aa50980b62405b.png)

このようにElementを使えば簡単にUIを作ることができるので非常に便利です。

以上でRails・Nuxt.js の環境をDockerでつくりElementの導入まで行いました。
読んでくださってありがとうございました。