tag:crieit.net,2005:https://crieit.net/boards/rails-form-helper/feed
「RailsガイドのActionViewフォームヘルパーをひたすら読み解いていく」の投稿 - Crieit
Crieitで「RailsガイドのActionViewフォームヘルパーをひたすら読み解いていく」ボードに投稿された最近の投稿
2020-12-13T18:37:58+09:00
https://crieit.net/boards/rails-form-helper/feed
tag:crieit.net,2005:PublicArticle/ebd08e0d7e36f6a083ac0a17da78b66c
2020-12-13T18:23:19+09:00
2020-12-13T18:37:58+09:00
https://crieit.net/boards/rails-form-helper/ebd08e0d7e36f6a083ac0a17da78b66c
【第2回】基本的なフォームを作成する
<h2>概要</h2>
<ul>
<li>Railsガイドの「1 基本的なフォームを作成する」を取り上げる
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://railsguides.jp/form_helpers.html#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">Action View フォームヘルパー - Railsガイド</a></li>
</ul></li>
<li>ちなみに、9章まである。12回ぐらいの連載で終わるかな</li>
</ul>
<h2>フォームヘルパーのヘルパーって?</h2>
<ul>
<li>この勉強をしたいと思ったのは、フォームについて勉強したいと思ったから</li>
<li>とはいえ、Railsガイドのタイトルは「Action View フォームヘルパー」
<ul>
<li>ということで、まずヘルパーについて簡単に触れる</li>
</ul></li>
</ul>
<p>Viewフォームヘルパーとは、ControllerやModelではなく、Viewで使うことを想定して、フォームを簡単かつ便利に作るためのヘルパー(道具)のこと。</p>
<p>例えば、content_tagヘルパーというものがあるのだけれど、これはまさにその典型例。</p>
<pre><code class="rb">content_tag(:div, "header", class: ["one", "two"])
#=> "< div class="one two" >header< /div >"
</code></pre>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://techracho.bpsinc.jp/hachi8833/2018_04_10/54701">Rails tips: ビューの`content_tag`のあまり知られていないオプション(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社</a></li>
</ul>
<p>という観点からすれば、フォームヘルパーとは、</p>
<ul>
<li><strong>フォームを簡単かつ便利に作るためのヘルパー(道具)のこと。</strong></li>
</ul>
<p>ということは、ヘルパー全般に言えるけれども、簡単かつ便利になる前の<br />
<strong>HTMLタグたちを理解しなければ、フォームヘルパーの意味は理解できない</strong>とうこと。</p>
<h2>基本的なフォームを作成する</h2>
<p>Railsガイドでは、まず<code>form_tag</code>の説明から始まる。<br />
ここで、<code>form_tag</code>, <code>form_for</code>, <code>form_with</code>の概要を確認する。</p>
<ul>
<li><code>form_tag</code>はモデルに紐づけない場合、form_forはモデルに紐付ける場合に使う</li>
<li>昔(Rails5.1より前)は、<code>form_for</code>と<code>form_tag</code>があった</li>
<li><code>form_for</code>と<code>form_tag</code>の2つは、<code>form_with</code>に統合された</li>
<li>この記事でも説明している
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2">【Rails 5】(新) form_with と (旧) form_tag, form_for の違い - Qiita</a></li>
</ul></li>
</ul>
<p>ということで、まずは<code>form_for</code>というモデルに紐づけないシンプルなケースを確認しよう。今では、<code>form_with</code>で書くことが一般的なので、その場合も確認する。</p>
<pre><code class="erb"># form_tag
<%= form_tag do %>
Form contents
<% end %>
# form_with
<%= form_with do %>
Form contents
<% end %>
</code></pre>
<pre><code class="html"><!-- Railsガイドで紹介されていたHTML -->
<form accept-charset="UTF-8" action="/home/index" method="post">
<div style="margin:0;padding:0">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</div>
Form contents
</form>
<!-- Rails 6系で試した場合、すこし違うHTMLが出力された -->
<form action="/" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="E3gz0DuIaaH2ah/jibBQN5pOw5fRWu/0EKopuZ3uCkpoxldUUdSGWt9fQeYnrtuRu0EwofSmrCSoQ3iSKmuwKQ==">
Form contents
</form>
</code></pre>
<p>前回の記事で触れたとおりだが、以下を確認しよう。</p>
<ul>
<li>action: フォームで送信するURL</li>
<li>method: HTTPのリクエストのメソッド(get, post, put, patch, deleteなど)</li>
<li>authenticity_token: CSRF対策のためのトークン。</li>
</ul>
<p>なお、前回記事では触れなかったが、下記のとおりである。<br />
そんなに重要ではないと思うけど一応確認する。</p>
<ul>
<li>accept-charset: 文字コードを指定するオプション</li>
<li>name="utf8"のinput: 文字コードを指定するオプション
<ul>
<li>IEはフォームのaccpet_charsetを無視して、ブラウザの文字コードと同じエンコードだと認識してたらしい</li>
<li>それを乗り越えるため、隠しパラメータで文字コードを指定する方法を採用していたらしい</li>
</ul></li>
</ul>
<p>デフォルトでは、<strong>現在のURLに対してPOSTメソッドでHTTPリクエストが送られる。</strong> なので、送信先のURLやメソッドに関する記述がなくとも、先ほどのようなHTMLタグが生成される。</p>
<h2>1.1 一般的な検索フォーム</h2>
<p>モデルに紐づかないフォームとしてありがちなのが、検索フォームだ。<br />
この場合、actionやmethodがデフォルトのままだと困るので、設定する必要がある。</p>
<p>また、検索フォームの場合、何のフォームであるか示すため、<br />
左横なんかに<strong>ラベル</strong>がついていることが一般的だと思う。</p>
<p>そして、当然検索ワードを入力するtextなどの入力フォームや送信ボタンが必要となる。</p>
<p>ということで、このような記述になる。<br />
せっかくなので、form_withバージョンで書いてみる。</p>
<pre><code class="erb"><%= form_with(url: "/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
</code></pre>
<pre><code class="html"><form accept-charset="UTF-8" action="/search" method="get">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
</div>
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>
</code></pre>
<p>GETメソッドの場合、parameterはURLにくっつけるような形で送信される。<br />
例えば、googleでmiketaと検索すると、以下のようなURLにリダイレクトされる。</p>
<pre><code class="text">[https://www.google.com/search?q=miketa&rlz=1C5CHFA_enJP891JP891&oq=miketa&aqs=chrome..69i57j69i59l2j69i60l4j69i65.551j0j15&sourceid=chrome&ie=UTF-8](https://www.google.com/search?q=miketa&rlz=1C5CHFA_enJP891JP891&oq=miketa&aqs=chrome..69i57j69i59l2j69i60l4j69i65.551j0j15&sourceid=chrome&ie=UTF-8)
</code></pre>
<p>不勉強なので詳しいことは分からないが、以下のようなパラメータが送られている。本筋から外れるのでこれ以上は踏み入らないが、各メソッドの違いを意識するとよい。</p>
<pre><code class="text">q: miketa
rlz: 1C5CHFA-en....
aqs: chrome..69i...
sourceid: chrome
ie: UTF-8
</code></pre>
<h2>1.2 フォームヘルパーの呼び出しで複数のハッシュを使う</h2>
<p>Railsガイドには、以下のように書いてある。</p>
<blockquote>
<p>form_tagヘルパーは2つの引数を取ります。<br />
1つはアクションへのパスで、もう1つはオプションのハッシュです。</p>
<p>このハッシュには、フォーム送信のHTTPメソッドやHTMLオプション(フォーム要素のクラスなど)が含まれます。</p>
</blockquote>
<p>引数とは、メソッドのカッコ内のやつです。</p>
<pre><code class="rb">form_tag(引数はここだよ)
</code></pre>
<p>引数は、アクションへのパスとオプションのハッシュの2つなので、こうなる。</p>
<pre><code class="rb">form_tag(
第1引数 - アクションへのパス(paramsの送り先のURL),
第2引数 - オプションのハッシュ(GETなどのHTTPメソッドやhtmlタグ内のclassの指定など
)
</code></pre>
<p>第1引数についてはURLを直書きすることもできるが、<code>users_path</code>のように書くこともできるし、<code>controller: "people", action: "search"</code>のように書くこともできる。</p>
<p>ただ、<code>controller: "people", action: "search"</code>を何気なく書くと、エラーになってしまう。例えば、以下のように書くとactionが第2引数と認識されるのでエラーになる。</p>
<pre><code class="rb">form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")
</code></pre>
<p>よって、ハッシュを使うとよい。</p>
<pre><code class="rb">form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
</code></pre>
<h2>余談:form_withの場合</h2>
<p>ぶっちゃけあまりform_tagなんて使わないと思うので、<br />
form_withについても理解しておきたい。</p>
<p>ということで、form_withメソッドについても調べてみた。</p>
<pre><code class="rb">form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
</code></pre>
<p>ここでRuby自体の理解が大事になるのだが、こういった引数の書き方はキーワード引数という。キーワード引数を指定をしないとき、デフォルトの設定が採用される。</p>
<p>例えば、root_pathへのフォームを作成する場合、このようなコードとなる。</p>
<pre><code class="erb"><%= form_with url: root_path do |form| %>
フォーム内容
<% end %>
</code></pre>
<p>この場合、例えばmodelについての引数は指定されていない。その場合、デフォルトの<code>model:nil</code>が採用される。他のオプションについても、同様である。</p>
<p>その他のオプション等が何を意味するかについては、長くなりそうなので、後ほどの機会で。</p>
<h2>1.3 フォーム要素生成に使うヘルパー</h2>
<p>分かりやすいので、そのまま掲載する笑</p>
<blockquote>
<p>Railsには、〜フォーム要素を生成するためのヘルパーが多数用意されています。<br />
これらの基本的なヘルパーは名前が_tagで終わっており (text_field_tagやcheck_box_tagなど)<br />
それぞれただ1つの<code><input></code>要素を生成します。</p>
</blockquote>
<p>ただ、どちらかというと、tagよりfieldの方が目にすることが多いかもしれない。<br />
fieldについては、後ほど触れる。</p>
<p>続ける。</p>
<blockquote>
<p>これらのヘルパーの1番目のパラメータは、inputの名前と決まっています。<br />
・・・たとえば、フォームに<code><%= text_field_tag(:query) %></code>というコードが含まれていたとすると、<br />
コントローラで<code>params[:query]</code>と指定すればこのフィールドの値にアクセスできます。</p>
</blockquote>
<p>少し具体的にみてみる。</p>
<pre><code class="erb"><%= form_with url:root_path do %>
<%= check_box_tag(:pet_dog) %>
<%= label_tag(:pet_dog, "I own a dog") %>
<%= check_box_tag(:pet_cat) %>
<%= label_tag(:pet_cat, "I own a cat") %>
<%= submit_tag("Search") %>
<% end %>
</code></pre>
<p>これで、こんなHTMLタグが生成される。</p>
<pre><code class="html"><form action="/" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="R0qYMo0//9dA1ECJ6G6muSXd9JXgss5z7DLPn6mwJL889Py252MQLGnhHoxGcC0fBNIHo8VOjaNU2560HjWe3A==">
<input type="checkbox" name="pet_dog" id="pet_dog" value="1">
<label for="pet_dog">I own a dog</label>
<input type="checkbox" name="pet_cat" id="pet_cat" value="1">
<label for="pet_cat">I own a cat</label>
<input type="submit" name="commit" value="Search" data-disable-with="Search">
</form>
</code></pre>
<p>そして、このフォームから送信されたparamsを取得するには、<br />
〜tagの第1引数を使って、こんな感じで書いてあげれば大丈夫。</p>
<pre><code class="rb"># pet_dogにチェックを入れて、pet_catにチェックをしていない場合
params[:pet_dog] #=> 1という値が取得できる
params[:pet_cat] #=> nilになる(paramsが送られていないため)
</code></pre>
<h2>1.4 その他のヘルパー</h2>
<p>HTMLのinputタグは、山ほど種類がある。<br />
20種類以上ある。</p>
<p>ということは、その数だけ、Railsのヘルパーもある。<br />
Railsガイドでは、これだけ紹介されているが、あくまで一例。</p>
<pre><code class="erb"><%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>
</code></pre>
<p>フォーム作成前に、どのようなinputタグがあるか、そしてそれに対応するrailsのヘルパーはどのようなものになるか、確認してみるといいかも。</p>
<h2>最後にテスト</h2>
<ul>
<li>フォームヘルパーは、HTMLの何を簡単に作るものでしょう?</li>
<li>getメソッドの場合、パラメータはどこに付与されるでしょう?
<ul>
<li>ここではざっくりとしか説明してないですが、postメソッドの場合などについても調べてみるとよい</li>
</ul></li>
<li>以下の場合、どうすればいいでしょう?
<ul>
<li>emailフィールドのフォームを設ける</li>
<li>コントローラでparams[:person]として値を取得したい</li>
<li>モデルに紐づけず、検索フォームとしたい</li>
<li>検索フォームは、<code>emails_controller.rb</code>のsearchアクションで処理する</li>
<li>適当な設定なので、おかしいところがあるかもしれないが、ご容赦ください</li>
<li>その場合、ツッコミどころを考えるのも勉強になります笑</li>
</ul></li>
<li>fieldの数を10個ぐらいあげてみよう!</li>
</ul>
<h2>補足</h2>
<p>やばい、この調子だと日がくれる...</p>
miketa_webprgr
tag:crieit.net,2005:PublicArticle/Form
2020-12-13T18:20:56+09:00
2020-12-13T18:25:56+09:00
https://crieit.net/boards/rails-form-helper/Form
【第1回】 Formの概要
<h2>概要</h2>
<ul>
<li>初回なので、Railsガイドの解説に突入する前に、フォーム自体についてまとめる</li>
</ul>
<h2>フォームとは</h2>
<ul>
<li>フォームとは、Webアプリを使う人が<strong>サーバーに何かを送るときに使う</strong></li>
<li>例えば、ユーザー登録のときにメールアドレスやパスワード登録をするときなど</li>
<li>ここなんかが分かりやすそうだった
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://html-coding.co.jp/annex/dictionary/html/form/">formタグとは|コーディングのプロが作るHTMLタグ辞典</a></li>
</ul></li>
</ul>
<p>初学者でも、何かを送るものって意識はあるだろうけど、以下の意識は薄いのではないか。<br />
フォームでは、<strong>何を・どこに・どのように・どんなinputで送るか</strong>が定義されている。</p>
<ul>
<li>どこのURLに</li>
<li>どのようなメソッドで</li>
<li>どのようなパラメータを送るか
<ul>
<li>keyとvalueは何か</li>
</ul></li>
<li>フォームの種類は何か
<ul>
<li>HTMLのinputで指定する</li>
<li>text_fieldなどがある</li>
<li>実は20種類以上もある。。</li>
</ul></li>
</ul>
<p>What, Where, How,( + Type of input)と覚えるとよいかも。(自己流です)<br />
2W1HT・・・なんかいい語呂でもないかな。。。</p>
<p>まず、フォームの概要について押さえておく。<br />
ここはRailsガイドに掲載されていないが、把握すべき基礎的な内容だ。</p>
<h2>formタグの概要について</h2>
<pre><code class="html"><form accept-charset="UTF-8" action="/users" method="post">
<input type="email" name="user[email]" value="[email protected]" />
<input type="text" name="user[name]" value="alice" />
<input type="text" name="user[age]" value="20" />
</form>
</code></pre>
<p>action: アクセス先のURL<br />
method: HTTPメソッド<br />
type: フォームの入力値の種類(text, email, radio, checkboxなど)<br />
name: フォームの名前を示している<br />
value: 値</p>
<p>アクションは、あくまでアクセス先のURLである。<br />
間違いやすいので注意すること。</p>
<p>paramsハッシュの中身は、以下のとおりとなる。</p>
<p><code>{'user' => {'email' => '[email protected]'}, {'name' => 'alice'}, {'age' => 20} }</code></p>
<p>このような形で書くことで、params[:user]と書くことで、Userモデルに紐づく全てのハッシュを取得できる。また、以下のように取得するハッシュを制限することができる。</p>
<pre><code class="rb">params.require(:user).permit(:email)
</code></pre>
<h2>Labelタグについて</h2>
<p>フォームでは、よくラベルタグを使う。<br />
フォームを使うメリットは、2つある。</p>
<ul>
<li>ラベルをクリックすると、そのチェックボックスをクリックしたことになる。</li>
<li>音声読み上げソフトが、いい感じに対応してくれる。</li>
</ul>
<p>ラベルタグは、インプットタグと紐付ける。<br />
紐付けにあたっては、labelのforとinputのidを同一にする。</p>
<p>以下の事例においては、cheeseで紐付けされている。<br />
繰り返しになるが、nameはパラメータのkeyなので、勘違いしないように。<br />
(keyというのは用語が適切でないかも。Hashのkey-valueのkey)</p>
<pre><code class="html"><div class="preference">
<label for="cheese">Do you like cheese?</label>
<input type="checkbox" name="cheese" id="cheese">
</div>
</code></pre>
<p>Form_withでは使われないが、labelタグにinputタグをネストすることでも紐付け可能。</p>
<pre><code class="html"><label>Do you like peas?
<input type="checkbox" name="peas">
</label>
</code></pre>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/HTML/Element/label"> - HTML: HyperText Markup Language | MDN</a></li>
</ul>
<h2>Formの隠し要素</h2>
<p>Railsでフォームを作ると、文字エンコードとauthenticity_tokenが自動で含まれる。<br />
authenticity_tokenはCSRF対策のため。GETメソッドの場合、このトークンは作成されない。</p>
<p>Railsサーバーに対して、このトークン抜きでリクエストを送っても拒否される。要注意。(ただし、Railsの設定次第ではトークン抜きでも受け付けることができたはず)</p>
<pre><code class="erb"><%= form_tag do %>
Form contents
<% end %>
</code></pre>
<pre><code class="html"><form accept-charset="UTF-8" action="/home/index" method="post">
<div style="margin:0;padding:0">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</div>
Form contents
</form>
</code></pre>
<h2>CSRFとは</h2>
<p>CSRFとは、クロスサイトリクエストフォージェリーの略。<br />
まず、リクエストフォージェリーに着目するとよさそう。</p>
<p>リクエストとは、サーバーに送るリクエストのこと。<br />
例えば、ユーザー登録をするのは、クライアントからサーバーへのリクエスト。</p>
<p>フォージェリとは、偽物のこと。</p>
<p>で、リクエストフォージェリー。<br />
これは、悪意のある人がJavaScriptなんかでこっそりコードを仕込んでおくことで、<br />
その人の意志とは関係なく、サーバー側にリクエストを送ってしまうこと。</p>
<p>え、そんなことできるのかって。できる。<br />
JavaScriptを使うと、例えば何かの要素をクリックした際に、<br />
特定のURLに対してリクエストを送るよう指示を出すことができる。</p>
<p>この機能を使って、例えばtwitterにログインしているユーザーを対象にして、<br />
クリックしたと同時に、そのユーザーに知らず知らずの内に、サーバー側にリクエストを投げることができる。</p>
<p>例えば、殺害予告ツイートの投稿リクエストを投げることができる。<br />
ログインした状態で、まさにそのユーザーからリクエストが送られてしまうので、<br />
このリクエストは受け入れられてしまい、意図していないのに殺害予告ツイートをしてしまうハメになる。</p>
<p>たぶん、概念的にはこんな感じの理解で大枠あっていると思う。</p>
<h2>authenticity_tokenでCSRFがなぜ対策できるのか</h2>
<p>試してみると分かるが、このtokenはサーバー側から毎回ランダムに生成される。<br />
同じページであっても、リロードすると変わるはずだ。</p>
<p>このトークンは、投稿時にサーバー側に併せて送られる。<br />
そして、このトークンがサーバー側で事前に送ったものと一致するか検証がなされる。</p>
<p>つまり、イメージとしては、こんなかんじ。<br />
まず、OKパターン。</p>
<ol>
<li>サーバー「ユーザー登録するときは、開けゴマって言うこと」</li>
<li>クライアント「開けゴマ! ユーザー登録します。」</li>
<li>サーバー「OK! 登録する!」</li>
</ol>
<p>NGパターン。</p>
<ol>
<li>クライアントがCSRFにひかかってしまった</li>
<li>意図していないのに「〇〇を17時に爆破する」というツイート登録がサーバーに飛ぶ</li>
<li>サーバー「開けゴマ!がないから受け付けません」</li>
</ol>
<p>この開けゴマがtoken。</p>
<p>これがリロードする度に変わるので、サイトをまたいで、つまりクロスサイトで<br />
フォージェリーなリクエストを送ることを防げる。</p>
<h2>最後にテスト</h2>
<p>まず、これが自分の言葉で説明できるか考えてみよう。</p>
<pre><code class="html"><form accept-charset="UTF-8" action="/users" method="post">
<input type="email" name="user[email]" value="[email protected]" />
<input type="text" name="user[name]" value="alice" />
<input type="text" name="user[age]" value="20" />
</form>
</code></pre>
<p>次に、ラベルって何か自分の言葉説明できるか考えてみよう。<br />
そして、空欄に何が入るか振り返ってみよう。</p>
<pre><code class="html"><div class="preference">
<label for="空欄">Do you like cheese?</label>
<input type="checkbox" name="cheese" id="cheese">
</div>
</code></pre>
<p>最後にauthenticity_tokenって何だったか自分の言葉で説明できるか考えてみよう。</p>
<h2>補足</h2>
<p>自分で書いておきながら説明できないかも。。。<br />
あと、これは書きダメベースなので、次回から尻すぼみます。</p>
miketa_webprgr