バリデーションエラーメッセージをJavaScript使って表示させる
いつもはフォームのエラーメッセージは下記のようにJavaScriptを使わずに行っていた。
messages_controller.rb
class MessagesController < ApplicationController
〜略〜
def create
@message = Message.new(message_params)
if @message.save
redirect_to root_path
else
render :new
end
end
〜略〜
end
new.html.erb
<%= form_with(model: @message, local: true) do |f| %>
<% if @message.errors.any? %>
<%= render 'errors', model: f.object %>
<% end %>
〜略〜
<% end %>
_errors.html.erb
< if model.errors.any? >
<div class="error-alert">
<ul>
<% model.errors.full_messages.each do |message| %>
<li class='error-message'><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
今回はJavaScriptを使ってエラー表示してみる。
必要なことは
①turbolinks を有効にする
②respond_toメソッドを使ってリクエストのフォーマットごとに処理を分ける
③local: true を消してリクエスト形式をJS形式に変える
④create.js.erb ファイルを作成し、要素を指定し、エラー表示の部分を埋め込む
⑤JS形式のときの部分テンプレートの読み込みは "j render" を使う
messages_controller.rb
class MessagesController < ApplicationController
〜略〜
def create
@message = Message.new(message_params)
respond_to do |format|
if @message.save
format.html { redirect_to root_path }
else
format.html { render :new }
format.js
end
end
end
〜略〜
end
new.html.erb
<%= form_with(model: @message ) do |f| %>
<div id="errors"></div>
〜略〜
<% end %>
create.js.erb
document.getElementById("errors").innerHTML = "<%= j render "shared/error_messages" %>"
_errors.html.erb
<div class="error-alert">
<ul>
<% @item.errors.full_messages.each do |message| %>
<li class='error-message'>
<%= message %>
</li>
<% end %>
</ul>
</div>
以上で問題なくバリデーションのエラーが表示された。
ちなみに参考にさせていただいたのはこちらのページ
テンプレートエンジン
ERB
Rubyに標準で使えるテンプレートエンジン。
ERBの見た目はHTMLとほぼ同じのため、既存のHTMLもそのまま使える。
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<script type="text/javascript" src="https://js.pay.jp/v1/"></script>
<%= stylesheet_link_tag 'application', media: 'all'%>
<%= javascript_pack_tag 'application' %>
</head>
<body>
<%= yield %>
</body>
</html>
おなじみのコード。
HTMLをシンプルなコードにしたテンプレートエンジン。
Rails標準搭載ではないのでgemパッケージをインストールして使う。
!!!
%html
%head
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
%title Title
= csrf_meta_tags
= csp_meta_tag
%script{:src => "https://js.pay.jp/v1/", :type => "text/javascript"}
= stylesheet_link_tag 'application', media: 'all'
= javascript_pack_tag 'application'
%body
= yield
end などの締めのコードを省けるので記述量は減る
既存のerb をhaml にしてくれるgem 'html2haml'もある。
逆にhaml からerb に翻訳してくれる参考にさせていただいているホームページはこちら。
Slim
Hamlより更に簡潔なコードになるテンプレートエンジン。
こちらもRails標準搭載ではないのでgemパッケージをインストールして使う。
gem'slimrails'
doctype html
html
head
title
| Title
= csrf_meta_tags
= csp_meta_tag
script[type="text/javascript" src="https://js.pay.jp/v1/"]
= stylesheet_link_tag 'application', media: 'all'
= javascript_pack_tag 'application'
body
= yield
こんな感じ。
ちなみにこちらのページはHTMLをHamlまたはSlimに翻訳してくれるページ。
タイムゾーンに関する話
1つ目はGitHubのOmniAuth の認証が進まなくて詰まった話
結論から言うと時間がずれていました。
PCのローカル環境の時間がずれていることで発生したようだ。
見てほしいのはこちら。
https://gyazo.com/fbcdfda4e4f76793242b60d8c604c9f2
0.2秒…これだけでエラーになるのか…
https://qiita.com/hirokishirai/items/5a43977a38ecd922bfb9
の記事を参考にさせていただいたが、最初は全部問題ないと思いきや、
情報通信研究機構さんのページ(https://www.nict.go.jp/JST/JST5.html)で調べると時間がずれていることが判明。
色々調べた結果、
.bash_profileにexport TZ="Asia/Tokyo" を記述すると良いとのこと。
ターミナルで vim ~/.bash_profile、 i(インサート)で編集。
export TZ="Asia/Tokyo" を入れて esc、:wq で保存して解決。
2つめは別のタイムゾーンの変更方法。
Rails でアプリケーションを作るとデフォルトはUTCで日本とは9時間のズレでデータベースに保存される。
では日本時間で保存したい場合はどうするかというと、 config/application.rb内に config.time_zone = "Tokyo" とconfig.active_record.default_timezone = :local を追加する。
class Application < Rails::Application
config.load_defaults 6.0
config.time_zone = "Tokyo"
config.active_record.default_timezone = :local
end
ちなみにconfig.time_zone = "Tokyo" だけを記述するとRails内の時間だけを変えることが出来る。
ちなみに上記の2つは違うシチュエーションで行った処理のため、どちらも同じように作用するのかは検証していない。
pythonで模写してみた
Pythonならyoutubeのアーカイブのチャット欄からコメントを取得できると思い、手を付けてみた。(実際はLIVEチャットの取得のみでアーカイブからではなかった)
どこをどう持っていけばよいのか参考にした動画を見ながら、Youtube DATA API のリファレンスで試していった。
正直いきなりやるものではないと思った笑
ただ、需要があるおかげでコードはyoutubeにあったので2日かけて少しずつ理解しながらなんとか模写。
(ちなみにここにコピーしたコードのインデントはスペースを開けてもブログのブラウザに反映されてないかも…)
こちらが模写したコード。
*1:_dt - self.publishedAt).total_seconds(
存在するかを確認できるexists?、present?、presenceメソッド
exists?メソッド
レコードの存在チェックだけを行う場合に使用する。
例えば
モデル名.exists?
でデータベースのテーブルの中にデータが存在するかを確認し、真偽をtrue,false で返してくれる。
また、
モデル名.exists?(条件)
にすると指定した条件でマッチするか確認し真偽を返してくれる。
条件にはテーブルのidや値を入れることもできる。
Purchase.exists?(3)
Purchase.exists?(item_id: @item.id)
present?メソッド
レコードの存在チェックを行った後にインスタンスを使って何か処理をする場合に使用する。
例えば
if @item.purchase.present?
のようにインスタンスを使う場合。
exists?とpresent? は似ていてインスタンスを使うかどうか以外は同じような挙動をしていると思う。
presenceメソッド
present?メソッドがtrueの時、レシーバ自身を返し、 false のときは nil を返してくれる。
例えば
pry(main)> Item.find(1).present?
Item Load (0.6ms) SELECT `items`.* FROM `items` WHERE `items`.`id` = 1 LIMIT 1
=> true
[18] pry(main)> Item.find(1).presence
Item Load (0.5ms) SELECT `items`.* FROM `items` WHERE `items`.`id` = 1 LIMIT 1
=> #<Item:0x00007fed38fe7540
id: 1,
item_name: "A",
item_text: "A",
category_id: 4,
condition_id: 3,
shipping_id: 2,
sender_id: 3,
delivery_date_id: 3,
price: 1000,
user_id: 1,
created_at: Thu, 10 Dec 2020 16:38:34 UTC +00:00,
updated_at: Thu, 10 Dec 2020 16:38:34 UTC +00:00>
というようにそのまま中身を返してくれるもの
コンソールやpry-railsの有用性
エラーが起きたときは"binding.pry"もしくは"rails c"で調べるとわかりやすい
current_userのidやparamsのitem_id、Itemモデルの値段を調べていた時のコンソールやbinding.pryの一部のコピー
rails c
1] pry(main)> p current_user.id
NameError: undefined local variable or method `current_user' for main:Object
from (pry):1:in `__pry__'
[2] pry(main)> p @item.user.id
NoMethodError: undefined method `user' for nil:NilClass
Did you mean? super
from (pry):2:in `__pry__'
[3] pry(main)> Item.find(current_user.id)
NameError: undefined local variable or method `current_user' for main:Object
from (pry):3:in `__pry__'
→結局current_userは定義されてないのでparamsの中の値が使えるbinding.pryを試す
binding.pry
11: def create
=> 12: binding.pry
13: @user_info = UserInfo.new(user_info_params)
14: if @user_info.valid?
15: pay_item
16: @user_info.save
17: redirect_to item_path(@item)
18: else
19: render action: :index
20: end
21: end
[1] pry(#<PurchasesController>)> @item = Item.find(params[:item_id])
CACHE Item Load (0.0ms) SELECT `items`.* FROM `items` WHERE `items`.`id` = 4 LIMIT 1 [["id", 4], ["LIMIT", 1]]
↳ (pry):1:in `create'
=> #<Item:0x00007fd3e171fdb0
id: 4,
item_name: "あ",
item_text: "あ",
category_id: 3,
condition_id: 2,
shipping_id: 2,
sender_id: 3,
delivery_date_id: 2,
price: 2222222,
user_id: 1,
created_at: Sun, 13 Dec 2020 12:07:44 UTC +00:00,
updated_at: Sun, 13 Dec 2020 12:07:44 UTC +00:00>
[2] pry(#<PurchasesController>)> current_user
=> #<User id: 2, email: "s@s.s", nickname: "SSS", last_name: "ササ", first_name: "ササ", last_name_kana: "ササ", first_name_kana: "ササ", birth_date: "1932-03-03", created_at: "2020-12-10 16:39:19", updated_at: "2020-12-10 16:39:19">
[3] pry(#<PurchasesController>)> current_user.id
=> 2
[4] pry(#<PurchasesController>)> current_user[:id]
=> 2
[5] pry(#<PurchasesController>)> @item.id
=> 4
[6] pry(#<PurchasesController>)> @item[:user_id]
=> 1
[7] pry(#<PurchasesController>)> item_id
NameError: undefined local variable or method `item_id' for #<PurchasesController:0x00007fd3c322d190>
Did you mean? item_url
from (pry):7:in `create'
[8] pry(#<PurchasesController>)> :item_id
=> :item_id
[11] pry(#<PurchasesController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"xc85a50Pk8j+cWdEUV28OzggqHNQFvEVCTcEYMTJYzK5UihjtmL1H5g5pfrtX+mNj9yDJNm36/cYbMMp858vRw==", "user_info"=>{"postal_code"=>"", "prefecture_id"=>"1", "city"=>"", "address_line"=>"", "building"=>"", "tel"=>""}, "controller"=>"purchases", "action"=>"create", "item_id"=>"4"} permitted: false>
[12] pry(#<PurchasesController>)> params[:item_id]
=> "4"
[13] pry(#<PurchasesController>)> Item.find(params[:item_id])[:price]
CACHE Item Load (0.1ms) SELECT `items`.* FROM `items` WHERE `items`.`id` = 4 LIMIT 1 [["id", 4], ["LIMIT", 1]]
↳ (pry):13:in `create'
=> 2222222
上記黄色と赤の部分の値が欲しいがために色々と試した(実際はもっといろいろ試しているが…)
今思うと当然当たり前のことがわかっていない。
@item = Item.find(params[:item_id])
を定義している以上、@item[:price] で値段は出てくる。
(というかそもそも@item.priceでいい)
VS上で試すとブラウザをリロードしなければならず、特にフォーム関連はいちいちフォームに入力したりするのが手間なので、今後はこっちで調べながら行こうと思う。
paramsについて(formオブジェクトのハッシュの外にあるパラメーターを使う)
[1] pry(#<PurchasesController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"NqmRcmfoe9upA8OR3srT7HzO4o5RCGi/dyCS2jPuhUNiHNhiEF8Z/868Byp8VXlvMF4aScovG8aZmEMPqmgnoQ==", "user_info"=>{"postal_code"=>"999-9999", "prefecture_id"=>"3", "city"=>"まち", "address_line"=>"", "building"=>"", "tel"=>"0000000000"}, "token"=>"tok_2a06ee1f23eb5808850767712ec3", "controller"=>"purchases", "action"=>"create", "item_id"=>"1"} permitted: false>
[2] pry(#<PurchasesController>)>
user_infoのハッシュ外にある"item_id"と"token"を使うためにmergeメソッドで
.merge(item_id: params[:item_id], token: params[:token])
と記述。
これだけだとitem_idとtokenは属性値が定義されていないエラーが出るので
attr_accessor :item_id, :token
これで2つのオブジェクトを使えるようになった。
コンソールやbinding.pryを用いるようになってようやくいろんなエラーに対応できるようになってきた気がする。