ディレクトリ構造
app/
├── controllers/
│ ├── api/ # API用のコントローラ
│ │ └── tasks_controller.rb # タスク管理のAPIエンドポイント
│ ├── tasks_controller.rb # タスク管理の通常のコントローラ
│ └── tops_controller.rb # トップページのコントローラ
├── views/
│ ├── tasks/ # タスク管理のビュー
│ │ ├── index.html.erb # タスク一覧ページ
│ │ ├── show.html.erb # タスク詳細ページ
│ │ └── ... # その他のタスク管理ビュー
│ └── tops/ # トップページのビュー
│ └── index.html.erb # トップページ
├── javascript/
│ ├── components/ # 再利用可能なVueコンポーネント
│ │ └── DraggableCalendar.vue # ドラッグ可能なカレンダーコンポーネント
│ └── entrypoints/ # エントリポイントとなるJavaScriptファイル
│ └── tasks.js # タスク管理機能のエントリポイント
└── models/
└── task.rb # タスクモデル
エントリポイントの設定
エントリポイントである tasks.js
は以下のように設定します。
// app/javascript/entrypoints/tasks.js
import { createApp } from 'vue';
import DraggableCalendar from '../components/DraggableCalendar.vue';
document.addEventListener('DOMContentLoaded', () => {
const el = document.querySelector('#task-calendar');
if (el) {
createApp(DraggableCalendar).mount(el);
}
});
ここで、DOMContentLoaded
イベントを使用してページが完全に読み込まれた後にVueアプリケーションをマウントすることで、マウント対象の要素がDOMに存在することを保証しています。なお、マウント対象の要素がDOMに存在することを保証しなくてもよい場合は以下の書き方も可能です。
import { createApp } from 'vue';
import DraggableCalendar from '../components/DraggableCalendar.vue';
const app = createApp(DraggableCalendar);
app.mount('#task-calendar');
Vueコンポーネントの作成
DraggableCalendar.vue
コンポーネントは以下のように定義します。
<template>
<div class="calendar">
<!-- カレンダーの各日に対して、ドラッグアンドドロップ可能な領域を作成 -->
<div v-for="day in days" :key="day" class="calendar-day">
<div class="drop-zone" @drop="drop($event, day)" @dragover.prevent @dragenter.prevent>
<!-- この日に割り当てられたタスクを表示 -->
<div v-for="task in tasks[day]" :key="task.id" draggable="true" @dragstart="dragStart($event, task)">
{{ task.name }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
tasks: {}, // 日付をキーとしたタスクのオブジェクト
days: [], // カレンダーの日付を格納する配列
};
},
methods: {
dragStart(event, task) {
event.dataTransfer.setData('task', JSON.stringify(task));
},
drop(event, day) {
const task
= JSON.parse(event.dataTransfer.getData('task'));
// タスクをその日に移動するロジック
},
},
};
</script>
<style scoped>
/* スタイリング */
.drop-zone {
/* スタイル定義 */
}
.draggable-task {
/* スタイル定義 */
}
</style>
Railsビューの準備
タスク管理機能に関連するRailsビューは、Vueコンポーネントを含めるためのマークアップを提供します。以下はapp/views/tasks/index.html.erb
の例です。
<!-- app/views/tasks/index.html.erb -->
<p style="color: green"><%= notice %></p>
<h1>Tasks</h1>
<div id="task-calendar">
<!-- Vueコンポーネントがここにマウントされます -->
</div>
<%= vite_javascript_tag 'entrypoints/tasks' %>
<div id="tasks">
<% @tasks.each do |task| %>
<%= render task %>
<p>
<%= link_to "Show this task", task %>
</p>
<% end %>
</div>
<%= link_to "New task", new_task_path %>
ここで、vite_javascript_tag
ヘルパーを使用して、tasks.js
エントリポイントを読み込んでいます。これにより、ページに DraggableCalendar
コンポーネントがマウントされ、ドラッグアンドドロップによるタスク管理が可能になります。
APIコントローラの設定
タスクデータをフェッチし、更新するためには、バックエンドに適切なAPIエンドポイントを設定する必要があります。以下はapp/controllers/api/tasks_controller.rb
の簡単な例です。
# class Api::V1::TasksController < ApplicationController
# ...
# end
# 上記の書き方も可能
module Api
module V1
class TasksController < ApplicationController
before_action :set_task, only: [:show, :update, :destroy]
def index
@tasks = Task.all
render json: @tasks
end
def create
@task = Task.new(task_params)
if @task.save
render json: @task, status: :created, location: api_task_url(@task)
else
render json: @task.errors, status: :unprocessable_entity
end
end
def update
if @task.update(task_params)
render json: @task
else
render json: @task.errors, status: :unprocessable_entity
end
end
def destroy
@task.destroy
head :no_content
end
private
def set_task
@task = Task.find(params[:id])
end
def task_params
params.require(:task).permit(:name, :description, :due_date)
end
end
end
end
このコントローラは、タスクの一覧表示、作成、更新、削除を行う基本的なAPIエンドポイントを提供します。フロントエンドのVueコンポーネントからこれらのエンドポイントにリクエストを送信することで、タスクデータを非同期に操作できます。
ルーティングの設定
Rails.application.routes.draw do
# 以下3行を追記
namespace :api do
resources :tasks, only: [:index]
end
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations',
passwords: 'users/passwords'
}
devise_scope :user do
delete '/user_logout', to: 'devise/sessions#destroy', as: :logout_user
end
resources :tops, only: %i(index)
resources :users, only: %i(index show)
resources :tasks
resources :blogs
root 'tops#index'
end
まとめ
この記事では、Rails 7とVue.js 3を組み合わせて、ドラッグアンドドロップによるタスク管理機能を実装する基本的な流れを説明しました。重要なポイントは、Vueコンポーネントの作成とマウント、RailsビューとAPIエンドポイントの設定です。これらのステップに従うことで、リッチなユーザーインターフェースとサーバーサイドのデータ処理を組み合わせたモダンなWebアプリケーションを構築できます。
参考記事