Rails 6 でTodoApp作るぜ part2
今回は、TodoAppの本体Taskモデル・Tasksコントローラー・各種ビューを作る
Taskモデル作成
テーブル設計
名称 | カラム名 | データ型 |
---|---|---|
タイトル | title | string |
内容 | description | text |
めちゃめちゃシンプルに!
% rails g model Task title:string description:text
こんな感じ(nullつけ忘れました)
class CreateTasks < ActiveRecord::Migration[6.0] def change create_table :tasks do |t| t.string :title t.text :description t.timestamps end end end
% rails db:migrate
nullはそのうちつける・・・
コントローラーとビュー
シンプルなCRUDってやつ!
% rails g controller tasks index show new edit
まずはRoutesを設定
[ config/routes.rb ]
Rails.application.routes.draw do root 'tasks#index' get 'tasks/index', to: 'tasks#index' resources :tasks end
resources
で名前付きルートやら一括設定root
でroot設定だぞ!!!事件が発生した。。
tasks/index
にアクセスするとtasks/show.html.slim
が表示されました。なので、get 'tasks/index', to: 'tasks#index'
で押し通ります。
※ひとまず完成と言えるとこまでは止まっちゃダメ、絶対。
コントローラー
超シンプルなアプリなのでバババババ馬場馬っと書く
細かい修正は最後にやります。
[ app/controllers/tasks_controller.rb ]
class TasksController < ApplicationController def index @tasks = Task.all end def new @task = Task.new end def create @task = Task.new(task_params) if @task.save redirect_to tasks_url, notice: "タスク「#{@task.title}」を登録しました。" else render :new end end def show @task = Task.find(params[:id]) end def edit @task = Task.find(params[:id]) end def update @task = Task.find(params[:id]) if @task.update(task_params) redirect_to tasks_url, notice: "タスク「#{@task.title}」を更新しました!" else render :edit end end def destroy @task = Task.find(params[:id]) @task.destroy redirect_to tasks_url, notice: "タスク「#{@task.title}」を削除しました。" end private def task_params params.require(:task).permit(:title, :description) end end
このくらいは何も見なくても書けるようになりました(^^
flashメッセージを設定したので、ちゃんと表示されるようにviewに記述します
[ app/views/layouts/application.html.slim ]
doctype html html head title | TodoApp = csrf_meta_tags = csp_meta_tag = stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' body .container - if flash.notice.present? # 追加 .alert.alert-success = flash.notice # 追加 = yield
こんな感じになります。
ビューズ
こんな感じにしていきます。
[ tasks/index.html.slim ]
h1 タスク一覧 .nav.justify-content-end = link_to '新規登録', new_task_path, class: 'nav-link' table.table thead.thead-default tr th タイトル th 登録日 th 編集日 th tbody - @tasks.each do |task| tr td = link_to task.title, task td = task.created_at td = task.updated_at td = link_to '編集', edit_task_path(task), class: 'btn btn-primary mr-3' = link_to '削除', task, method: :delete, data: { confirm: "タスク「#{task.title}」を削除します。よろしいですか?" }, class: 'btn btn-danger'
tasks/index.html.slim
では一覧機能を表示させます。
@tasksで取得したデータをeach do
で全表示させる
表示内容は、とりあえず :title
、:created_at
、`:updated_at
の3つとします。
一覧ページで編集へのリンクを貼る際は、edit_task_path(task)
と引数を指定しないとエラーが出る
続いて、詳細ページ
[ tasks/show.html.slim ]
h1 タスクの詳細 .nav.justify-content-end = link_to '一覧', tasks_path, class: 'nav-link' table.table.table-hover tbody tr th タイトル td = @task.title tr th 内容 td = @task.description tr th 登録日 td = @task.created_at tr th 更新日 td = @task.updated_at = link_to '編集', edit_task_path, class: 'btn btn-primary mr-3' = link_to '削除', @task, method: :delete, data: { confirm: "タスク「#{@task.title}」を削除します。よろしいですか?" }, class: 'btn btn-danger'
showページでは、内容(:description)
も表示されるようにする
また、show では@task = Task.find(params[:id])
でデータを取得しているため、編集へのリンクに引数がいらない
逆に削除へのリンクではtask
ではなく@task
にする
次は_form.html.slim
を作成する
[ app/views/tasks/_form.html.slim ]
= form_with model: task, local: true do |f| .form-group = f.label :title, 'タイトル' = f.text_field :title, class: 'form-control', id: 'task_title' .form-group = f.label :description, '詳細' = f.text_area :description, class: 'form-control', id: 'task_description' = f.submit '登録', class: 'btn btn-success'
この書き方も暗記ですね。何も見なくても書けるようにします。
コントローラで設定した、task_params
と内容を合わせることを忘れずに!
[ tasks_controller.rb ]
(省略) private def task_params params.require(:task).permit(:title, :description) end end
tasks_params
の中のpermit(:title, :description)
で設定してるカラムしか保存されない
そして、newとeditを実装する
[ tasks/new.html.index ]
h1 タスク新規登録 .nav.justify-content-end = link_to '一覧', tasks_path, class: 'nav-link' = render partial: 'form', locals: { task: @task }
[ tasks/edit.html.index ]
h1 タスクの編集 .nav.justify-content-end = link_to '一覧', tasks_path, class: 'nav-link' = render partial: 'form', locals: { task: @task }
ここでは、= render 'form'
とはせずに、= render partial: 'form', locals: { task: @task }
と記述しています。
こうすると「インスタンス変数@taskを、パーシャル内のローカル変数taskとして渡す」ことができます。
= render 'form'
としたい場合は、
[ app/views/tasks/_form.html.slim ]
= form_with model: @task, local: true do |f| #@taskに変更する .form-group = f.label :title, 'タイトル' = f.text_field :title, class: 'form-control', id: 'task_title' .form-group = f.label :description, '詳細' = f.text_area :description, class: 'form-control', id: 'task_description' = f.submit '登録', class: 'btn btn-success'
model: task
をmodel: @task
にする必要があります。
以上でシンプルなCRUD機能の完成です。
ヘッダーの作成
ちょっと寂しいので簡単なヘッダーを作ります
ファイルapp/views/layouts/_header
を作成します。
[ layouts/_header ]
header.mb-3 .app-title.navbar.navbar-expand-md.navbar-dark.bg-dark .navbar-brand TodoApp
そして、application.html.slim
のbodyの下(.container
の上)にrenderします。
[ layouts/application.html.slim ]
doctype html html head title | TodoApp = csrf_meta_tags = csp_meta_tag = stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' body = render 'layouts/header' # 追加 .container - if flash.notice.present? .alert.alert-success = flash.notice = yield
ひとまず、よしっ
モデルに検証機能を加える
今のままだと、新規登録画面で何も書かなくても登録できてしまいます。それだと困る・・ことは現状ないですが、実際にwebサービスとして運用するとなるとあまりよろしくありません。
なので:title
と:description
には何も入力されていなければエラーになるように設定します。
[ app/models/task.rb ]
class Task < ApplicationRecord validates :title, presence: true, length: { maximum: 30 } validates :description, presence: true end
presence: true
とすることで入力必須項目となり、エラーを発生させることができます。
title
の方にはlength: { maximum: 30 }
も気分でつけてみました。これにより、タイトルは30文字以内にしないとエラーが出るようになります。このTodoAppは私の思うがままです。(笑)
このままでは、コンソールを開かないとなんのエラーが出たのかわからないのでviewで表示されるように設定します。
[ app/views/tasks/_form.html.slim ]
- if task.errors.present? ul#error_explanation - task.errors.full_messages.each do |message| li = message = form_with model: task, local: true do |f| .form-group = f.label :title, 'タイトル' = f.text_field :title, class: 'form-control', id: 'task_title' .form-group = f.label :description, '詳細' = f.text_area :description, class: 'form-control', id: 'task_description' = f.submit '登録', class: 'btn btn-success'
formに入力された値が正しくなければエラーを出す。ということで_form.html.slim
にブチ込みました。
もしエラーがあれば、それらのエラーをあるだけ全部伝えてくれ!
という内容ですね。
今回はここまで。
次回 User作る