カテゴリー: BackEnd

Vue.js入門その4〜TODOアプリにサーバーサイドを追加してみる〜

はじめに

最近Vue.jsの勉強をしているtonnyです。
前回は、DockerでRails + Vue.jsの環境を作ってみる記事を書かせていただきました。

今回はその環境を使用して、以前のTODOアプリにサーバーサイドAPIを追加したいと思います。

今までの記事はこちらになります。

準備

今回作成したいもの

動きとしては、以前のTODOアプリと同じです。

  1. タスク一覧画面
  2. タスク新規登録機能
  3. タスク完了機能(論理削除)

環境構築

今回は、DockerでRails + Vue.jsの環境を作ってみるをそのまま使おうと思いますので、環境構築の詳細は割愛します。

Rails環境がすでに整っている方は、下記のコマンドでVue.js込みの新規プロジェクトを作成できます。

$ rails new my-project --webpack=vue

また、Node.jsも使用しますので、合わせてインストールしておいてください。

サーバーサイド

Scaffoldを使用して省エネ実装したいと思います。
また、Vue.js部分のコードを記事で書きたい関係上、Rails部分のコードはGithubリンクのみで失礼します。
(記事が無駄に長くなってしまうので。。。)

DB

タスクは、タスク名と完了したかどうかのフラグだけあれば良いかと思いますので、下記のような簡単なテーブルを作成します。

No カラム名 NULL制約、デフォルト メモ
1 id INTEGER NOT NULL、AUTO INCREMENT
2 name VARCHAR NOT NULL タスク名
3 is_done TINYINT NOT NULL、DEFAULT 0 完了フラグ(0: 未完了、1: 完了)
4 created_at DATETIME NOT NULL 作成日
5 updated_at DATETIME NOT NULL 更新日

メモ:rails generateで余分なファイルを生成しない

Scaffoldで生成していくと、CSSやJS、テストなども生成されます。
これはこれで便利なのですが、今回のように特に実装する予定がないときや、自分で作成したい場合などは、いささか鬱陶しいです。

config/application.rbに追記することで、rails generateする場合の生成ファイルを制御できます。

今回はassetsファイルとhelper、およびテストを生成しないようにしました。

module RailsVuePractice
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.1

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
+   config.generators do |g|
+     g.assets false
+     g.helper false
+     g.test_framework false
+   end
  end
end

作成したファイル

APIなので、routes.rbではnamespaceを切ってルーティングを生成します。
例えば、タスク一覧を取得するAPIは、/api/tasksといった具合になるようにします。

Scaffoldで生成される、余分なアクションは消去しておきます。

また、JSONで返却したいので、ビューはjbuilderを使用しました。

試しに数件レコードを登録して、curlで確認してみてください。

$ curl -XGET localhost:3000/api/tasks
{"server_time":1502633984,"return_code":0,"tasks":[{"id":1,"name":"test1","is_done":false,"created_at":"2017-08-13T14:18:23.000Z","updated_at":"2017-08-13T14:18:23.000Z"},{"id":2,"name":"test2","is_done":false,"created_at":"2017-08-13T14:18:33.000Z","updated_at":"2017-08-13T14:18:33.000Z"}]}

作成、編集したファイルはGithubをご覧ください。
https://github.com/naoki85/rails-vue-practice/tree/todo_application

ビューの作成

JQueryとBootstrapを取得

今回もBootstrapを使用したいので、Gemfileに記載してbundle installします。
Rails 5.1からはJQueryがデフォルトから外れてしまったので、JQueryもインストールしておきます。

gem 'jquery-rails'
gem 'bootstrap-sass', '3.3.6'
/*
 * 省略
 *= require_tree .
 *= require_self
 */
/* Bootstrap */@import "bootstrap-sprockets";
@import "bootstrap";
// 省略
//= require jquery
//= require bootstrap-sprockets
//= require rails-ujs
//= require turbolinks
//= require_tree .

もとになるビューを作成

まずは、TODOアプリを描画する部分のコントローラーとビューファイルを作成します。

class TodoController < ApplicationController
  def index
  end
end
<h1>All Tasks</h1>
<table id="todo-index" class="table table-striped">
  <thead>
  <tr>
    <th>Name</th>
    <th></th>
  </tr>
  </thead>
  <tbody>
  <tr v-for="task in tasks" v-if="!task.is_done">
    <td>{{ task.name }}</td>
    <td>
      <div class="btn btn-danger" v-on:click="doneTask(task.id)">Done!</div>
    </td>
  </tr>
  <tr>
    <td><input v-model="newTask" class="form-control"></td>
    <td>
      <div class="btn btn-default" v-on:click="createTask">Create Task</div>
    </td>
  </tr>
  </tbody>
</table>

<%= javascript_pack_tag 'todo_vue' %>

ビューファイルは、前回のVue.js入門その3〜簡単にTODOアプリを作ってみたよ〜を使いました。

なお、javascript_pack_tagと記載することで、後ほど作成するVue.jsのファイルを読み込むことができます。

Vue.jsのファイルの作成

webpackでインストールすると、appディレクトリの下に、javascriptディレクトリができているかと思います。
さらにそのディレクトリの下に、packsというディレクトリがあります。

その中に今回はtodo_vue.jsというファイルを作成します。

まずはVueインスタンスにdataプロパティを持たせるところまで記載します。

// Vue.js本体をインポートします。
import Vue from 'vue'

var app = new Vue({
  el: '#todo-index',
  data: {
    tasks: [],
    newTask: ''
  },
});

tasksプラパティは、はじめは空配列にしておき、インスタンス生成時にAjaxでタスク一覧を取得したいと思います。

Ajax通信に便利なaxios

axiosは、Ajax通信ライブラリです。
まずはこれをインストールします。

$ npm install axios

さて、それではインスタンスが生成されたタイミングで、タスク一覧を取得したいと思います。

まずはaxiosをインポートすることで、使用可能になります。
Vue.jsにはcreatedフックがあるため、そちらに登録してあげます。

import Vue from 'vue'
import axios from 'axios';

var app = new Vue({
  el: '#todo-index',
  data: {
    tasks: [],
    newTask: ''
  },
  created: function() {
    app.fetchTasks();
  },
  methods: {
    fetchTasks: function() {
      axios.get('/api/tasks').then(function(response) {
        // success
        for(var i = 0; i < response.data.tasks.length; i++) {
          app.tasks.push(response.data.tasks[i]);
        }
      }, function() {
        // error
        alert('Sorry, server error occurred. Please reload.')
      });
    }
  }
});

後々、一覧表示は使用しそうなので、methodsに登録しておきます。

axiosからgetでつなぐことでGETリクエストできます。
このあたりはJQueryと似たような感覚で使用できます。

新規登録と完了も実装してしまう

基本的には流れは、

  • 新規作成、もしくは更新のAPIを叩く
  • レスポンスが返ってきたら、再度一覧のAPIを叩く

とします。
(再度一覧のAPIを叩くことは良くないので、改善の余地があります。)

  methods: {
    fetchTasks: function() {
      axios.get('/api/tasks').then(function(response) {
        // success
        for(var i = 0; i < response.data.tasks.length; i++) {
          app.tasks.push(response.data.tasks[i]);
        }
      }, function() {
        alert('Sorry, server error occurred. Please reload.')
      });
    },
+   createTask: function () {
+    axios.post('/api/tasks', { task: { name: app.newTask } }).then(function() {
+        app.refreshTasks();
+      }, function() {
+        alert('Saving a Task is failed')
+      });
+    },
+    doneTask: function (task_id) {
+      axios.put('/api/tasks/' + task_id, { task: { is_done: 1 } }).then(function() {
+        app.refreshTasks();
+      }, function() {
+        alert('Saving a Task is failed')
+      });
+    },
+    refreshTasks: function() {
+      app.tasks = [];
+      app.fetchTasks();
+    }
  }
});

再度一覧APIを叩いてリロードするのは、refreshTasks()という関数にしています。

ここまで実施していただければ、Vue.js入門その3〜簡単にTODOアプリを作ってみたよ〜と同じような動きをしつつ、DBに保存できるかと思います。
ソースコードはこちらになります。

https://github.com/naoki85/rails-vue-practice/tree/todo_application

さいごに

前回の記事を少し改良してみました。

まだ、コンポーネントは使用していないので、次回はコンポーネントに関して記載できればな、と思います。

naoki85

シェア
執筆者:
naoki85
タグ: VuejsRails

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前