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="&#x2713;" /> <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="&#x2713;" /> </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="&#x2713;" /> <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