はじめに
画面を操作してユーザーの情報を作成・編集ができるようにします。
railsの基本的な機能をなぞる箇所が多いのでrailsの説明は少なめになります。
今回の実装の中で理解が不十分な箇所が見つかった場合はrails tutorialやprogateを見返してみてください。
ゴールの確認
ちょっと横着して作成も編集の同じ/userのページに実装します。
初めてuserを作成するときは作成を。それ以降は編集機能にします。
ここでの差分はgithubにあげています。困った際に参照ください
とにかく画面を映す
まずは/userを指定したときにどんな形でもいいので画面が表示されるようにしましょう。
今の時点で/userにアクセスすると当然エラーになります。
No route matches [GET] "/user"
routeの設定
エラーを見るにrouteが設定されていないとのことなのでrouteを設定します
# frozen_string_literal: true
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
resource :user, only: [:show]
end
早速ですが、補足しておきたいことがあります。
今の段階で解放したいrouteは/userのみですが、resourceを使っています。
routeの書き方としてはget '/patients/:id', to: 'patients#show', as: 'patient'
のようにリクエスト名にパスを指定しながら書く方法がありますが、特段の理由がない限りresourcesを使った書き方にこだわります。生成されるパスに規則性が生まれますし、新しくパスを増やすたいときに少しの変更で追加できるので基本的にはresourcesを使ってパスを増やしていくのがよいでしょう。
また、resources(複数形)ではなくresource(単数系)のメソッドを使っている点にも注意が必要です。
resourcesとresourceでは生成されるパスが異なります。
今回はuserはポートフォリオ管理者の情報のみを作成・編集するページでuserは必ず一人です。そのためindexページは不要ですし、編集ページにおいても必ず一人のみを編集するため、パラメータにidがなくてもレコードを特定することができます。
このようなユースケースのときにresourceメソッドを採用すると最小限のパスを提供してくれます。
それでは改めて/userにアクセスしてみましょう。
まだエラーになります。
uninitialized constant UsersController
Controllerの作成
エラーの内容からコントローラがないとのことなのでコントローラを作成しましょう
class UsersController < ApplicationController
def show
end
end
それではまだ/userにアクセスしてみます。
まだエラーになりますね。
UsersController#show is missing a template for request formats: text/html
テンプレートの作成
エラーの内容からアクションに該当するテンプレートファイルがないとのことなので作成しましょう
<div>
this is user edit page
</div>
/userにアクセスしてみましょう。
今度は表示されました。
bootstrapのインストール
ここから、formをこの画面に作成していくのですが、一からそれなりに使いやすいフォームをhtmlで実装していくのは大変です。この先もhtmlやcssで時間を使われるのは辛いですし、ないよりこの画面はポートフォリオ管理者(自分自身)しか見ることはないので見た目にそこまでこだわりたくはないです。
そこで、cssのフレームワークであるbootstrapをインストールしたいです。
インストール方法
インストールの方法はrubygem.orgのbootstrapのドキュメントを参照してもらうのが最新のより正しい情報を参照できてよいです。
インストール方法はバージョンによって大きく変わってしまいます。
また、つまづきポイントもそれなりにあると思われます。
全ての手順を上から正確にできているか。そもそもbundle installが成功しているか
それでも動作しない場合はdocker compose up –buildをしてイメージを作成し直してみてください
私がbootstrapをインストールする過程で生じた差分を載せておきます
作成機能
作成機能を作るにあたってフォームをまず作ります。bootstrapを使って見た目のフォームを先に作ってからそのフォームにrubyを埋め込んでいきます。
最終的なhtml.erbを載せておきます。この時点だと必要なメソッドがコントローラで実装されていないのでエラーになると思います。
<div class="container">
<main>
<div class="row g-5 py-5">
<div class="col-md-7 col-lg-8">
<h4 class="mb-3">Intoduction</h4>
<%= form_with model: user, url: user_path do |f| %>
<div class="row g-3">
<div class="col-12">
<%= f.label :name, "Name", class: "form-label" %>
<%= f.text_field :name, class: "form-control" %>
<div class="invalid-feedback">
Valid first name is required.
</div>
</div>
<div class="col-12">
<%= f.label :email, "Email", class: "form-label" %>
<%= f.email_field :email, class: "form-control", placeholder: "you@example.com" %>
<div class="invalid-feedback">
Please enter a valid email address for shipping updates.
</div>
</div>
<div class="col-sm-6">
<%= f.label :postal_code, "Postal Code", class: "form-label" %>
<%= f.text_field :postal_code, class: "form-control" %>
</div>
<div class="col-12">
<%= f.label :address, "Address", class: "form-label" %>
<%= f.text_field :address, class: "form-control", placeholder: "1234 Main St" %>
<div class="invalid-feedback">
Please enter your shipping address.
</div>
</div>
<hr class="my-4">
<%= f.submit "Submit", class: "w-100 btn btn-primary btn-lg" %>
<% end %>
</div>
</div>
</main>
</div>
これを開くためにusersコントローラへ下記を実装してみてください
def user
@user ||= User.first || User.new
end
helper_method :user
では、フォームが送信されたときのrouteとアクションを実装していきます
# frozen_string_literal: true
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
resource :user, only: %i[show create]
end
class UsersController < ApplicationController
def show
end
def create
user.assign_attributes(user_params)
if user.save
redirect_to user_path
else
render :show
end
end
def user
@user ||= User.first || User.new
end
helper_method :user
def user_params
params.require(:user).permit(:name, :email, :postal_code, :address)
end
end
フォームに値を入力して送信をしてみてください。レコードが作成されるでしょうか?
もし、すでにレコードを作成済みの場合はDBクライアントツールから直接削除して試してみてください
編集機能
それでは編集機能を作成します
routeに編集用のパスを追加して、アクションをコントローラに実装します
# frozen_string_literal: true
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
resource :user, only: %i[show update create]
end
class UsersController < ApplicationController
def show
end
def create
user.assign_attributes(user_params)
if user.save
redirect_to user_path
else
render :show
end
end
def update
user.assign_attributes(user_params)
if user.save
redirect_to user_path
else
render :show
end
end
def user
@user ||= User.first || User.new
end
helper_method :user
def user_params
params.require(:user).permit(:name, :email, :postal_code, :address)
end
end
作成した後、さらに値を書き換えてフォームを送信してみてください。その値がレコードに反映されることを確認しましょう。
同じフォームを使ってupdateとcreateのパスに都合よくアクションが分かれるのはrailsのform_withのおかげです。form_withの引数に入れているインスタンスがすでにレコードが作成されている場合はputリクエストを。まだレコードが作成されていないインスタンスの場合はpostリクエストを送信する仕様になっています。
フラッシュ
今のままだと、作成、編集に成功・失敗したかどうかが分かりにくいのでフラッシュメッセージを入れます。
方針としては、flashがある場合にメッセージを表示させるためのパーシャルを用意して、それを一番application.html.erbに配置します。
コントローラでflashメッセージを仕込むためのコードを入れておきます
<% flash_mapper = {success: "alert-success", error: "alert-danger" }%>
<% flash.each do |type, message| %>
<div class="alert <%= flash_mapper.fetch(type.to_sym, "alert-primary") %>" role="alert">
<%= message %>
</div>
<% end %>
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= render partial: "shared/flash_message" %>
<%= yield %>
</body>
</html>
class UsersController < ApplicationController
def show
end
def create
user.assign_attributes(user_params)
if user.save
flash[:success] = "Record successfully created"
redirect_to user_path
else
flash.now[:error] = "Failed to create Record"
render :show
end
end
def update
user.assign_attributes(user_params)
if user.save
flash[:success] = "Record successfully updated"
redirect_to user_path
else
flash.now[:error] = "Failed to update Record"
render :show
end
end
def user
@user ||= User.first || User.new
end
helper_method :user
def user_params
params.require(:user).permit(:name, :email, :postal_code, :address)
end
end
まとめ
なるべく自分が実装する手順に近い順番で紹介をしてきましたが、もっと細かく動作確認を繰り返しながら実装しています。特にcontrollerについてはbinding.pryを入れてコンソールで試してうまくいったコードをソースコードに順番に書いていっています。
いろんなところでブレークポイントを駆使してメソッドの返り値や動作を確かめつつ実装していくとrubyやrailsへの解像度も高まると思います。
コメント