Rails View基礎まとめ
Railsのビューの基本的な知識をまとめました。
理解すべきことは以下です。
- ビューはMVCのViewのことで、画面の表示を担当する
- Railsのビューではerbかslimというテンプレートエンジンを使う
application.html.erbとyieldを使うことでレイアウトを共通化できる- パーシャルを使うことでビューを使い回すコンポーネントとして作成できる
- ヘルパーを使うことで柔軟にビューの要素を作成できる
ビューとは
ビューとは、MVCのViewにあたり、アプリの表示を行う部分。
ビューファイルはapp/views以下に作る。
ビューファイルは1つのコントローラにつき1つのディレクトリが作られる。
例えばProductsControllerを作成したとすると、app/views/productsが作られる。
テンプレート
テンプレートとは、HTMLを生成するためのファイルで、Railsのコードを埋め込むことができる。
RailsではERBかSlimを使う。ERBはデフォルトで使えるテンプレートで、Slimはgemをインストールして使う。
ERB
ERBはRailsでデフォルトで使われるテンプレート。拡張子が.erbとなる。
HTMLの書き方は同じだが、<% %>や<%= %>を使うことで、Rubyのコードを書くことができる。
ERBの例。
<% @articles.each do |article| %> <h2><%= article.title %></h2> <p><%= article.content %></p> <% end %>
<% %>は実行結果を出力せず、<%= %>は出力する。
slim
Slimは軽量でシンプルなテンプレートエンジンで、ERBよりも簡潔な構文を提供し、HTMLコードを短く記述できる。拡張子は.slim。
Slimだと以下のように書ける。
- @articles.each do |article| h2 = article.title p = article.content
インストール
Slimはgemをインストールする必要があるので、gem install slimを実行する。
設定
以下の設定を記述することで、自動的にslimファイルが作られるようにする。
module App
class Application < Rails::Application
config.generators.template_engine = :slim #slimに変更
end
書き方
SlimだとHTMLの<>が不要になる。
主な記法
- タグ → h2
- テキスト → |
- class → .
- id → #
- コメント → /
- <%= %> → =
- <% %> → -
クラスやidは連続して繋げることができる。
またif文やeach文ではendが不要で、renderとyieldを使うときは== render、== yieldのように=を2つ書く。
レイアウト
デフォルトのviews/application.html.erbを使うことで、アプリ全体の共通のレイアウトを作ることができる。
Railsアプリ作成時のapplication.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Myapp</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>
<%= yield %>
</body>
</html>
<%= yield %>が個別のレイアウトを読み込む部分。
head内のタグは後述のヘルパーで解説。
異なるレイアウトを使いたい場合は、コントローラで使用するレイアウトファイルを指定する。
管理画面のレイアウトを作るとする。
views/layouts/admin.html.erbを作成する。
以下の記述でレイアウトを指定する。
class AdminController < ApplicationController layout :admin end
これによって、AdminControllerのアクションで読み込まれるビューはadmin.html.erbを使うようになる。
パーシャル
パーシャルはヘッダーや、エラーメッセージなど使い回すようなコンポーネントを作る仕組み。
1ファイルに1つのコンポーネントを作り、それらを使うときは以下のようにrenderを使って読み込む。
<%= render 'header' %>
_header.html.erbのように、パーシャルのファイル名の先頭には_をつける必要がある。
またヘッダーやフッターなどのレイアウトに関連するパーシャルはapp/views/layoutsに、エラーメッセージのような機能的なコンポーネントはapp/views/sharedに保存するのが通例。
パーシャルは以下のように変数を渡すこともできる。
<%= render 'shared/error_messages', object: @article %>
このような書き方で、エラーメッセージを共通化できる。
ヘルパーメソッド
ビューではデフォルトで使えるヘルパーメソッドがある。
link_to
link_toはリンクを生成するヘルパー。第一引数に表示するテキスト、第二引数にリンク先のURLを指定する。
<%= link_to 'Home', root_path %>
以下のHTMLが生成される。
<a href="/">Home</a>
画像、アイコンなどをリンクで使いたい場合は、以下のようにブロックを使う。
<%= link_to root_path do %> <!-- 画像やアイコン --> <% end >
button_to
button_toは指定したURLに送信するフォームのボタンを生成するヘルパー。ボタンだけじゃなく内部的にフォーム自体を生成する。第一引数にボタンのテキスト、第二引数に送信先のURLを指定する。
<%= button_to 'Home', root_path %>
以下のHTMLが生成される。
<form class="button_to" method="post" action="/"> <button type="submit">Home</button> <input type="hidden" name="authenticity_token" value="YFRveWNWHk4OS83NWZ5Fw9IyF3E0SRM79o7Lf3_KQA8I3rk4h-FDGvT-_tCGq9p7Lc7_AdmmMwhuR_QxnYdJtA" autocomplete="off"> </form>
formタグの中にbuttonとinput type="hidden"が作られている。actionに送信先のURLが指定される。
input type="hidden"はCSRF対策のための隠しフィールド。
image_tag
image_tagは画像を生成するヘルパー。第一引数に画像のパスを、第二引数にはオプションとしてalt属性やclass属性、sizeなどを指定する。
画像ファイルはapp/assets/images以下におく。
<%= image_tag 'avatar.png' %>
以下のようなHTMLが生成される。
<img src="/assets/avatar-afd90c6c76b3ffd5fb8e9bee941953b8057ca113083745e38900e121e76cf728.png">
数字とアルファベットの羅列の部分はアセットパイプラインによるコンテンツのハッシュ。これによってファイルが一意に識別され、キャッシュされることなく常に最新の内容が提供される。
後述するstylesheet_include_tagとjavascript_include_tagを使ったCSSとJavaScriptファイルの生成時にも使われる。
form_with
form_withはモデルから関連するフォームを生成するヘルパー。第一引数にモデルのインスタンスを渡す。
<%= form_with(model: @article) do |f| %>
<div>
<%= f.label :title, 'タイトル' %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :content, '内容' %>
<%= f.text_area :content %>
</div>
<div>
<%= f.submit "投稿" %>
</div>
<% end %>
以下のHTMLが生成される。
<form action="/articles" accept-charset="UTF-8" method="post"> <input type="hidden" name="authenticity_token" value="Y0YADiht4fBLcOQFg-LNM46ZcPfluCPNyuJRpqJoC01tkOvzPY7G3Wu1IBjRxTZi5mLNR9rEKB06xPQvxgxqJw" autocomplete="off"> <div> <label for="article_title">タイトル</label> <input type="text" name="article[title]" id="article_title"> </div> <div> <label for="article_content">内容</label> <textarea name="article[content]" id="article_content"></textarea> </div> <div> <input type="submit" name="commit" value="投稿" data-disable-with="投稿"> </div> </form>
form_withが使われる前は、フォームに関連するモデルがない場合はform_tagを、関連するモデルがある場合はform_forを使っていたが、form_withではどちらも可能。
form_tagとform_forはRails5.1から非推奨。
モデルを渡すと、URLとスコープを自動で推測する。モデルがデータベースに存在すればupdateアクションを、存在しなければcreateアクションのURLに送信する。
スコープはname属性の部分。
form_withはデフォルトで非同期通信のリクエストで送信される。これを無効にするにはlocal: trueを指定する。
content_for
content_forは動的なコンテンツを挿入するヘルパー。
例えばページのタイトルを表示するビューによって変更したい場合。
以下のようにタイトルを表示したい場所でyield(:title)としてタイトルの値を受け取るようにしておく。|| "Default Title"の部分はタイトルを設定しない場合にデフォルトで表示するタイトル。
<!DOCTYPE html> <html> <head> <title><%= yield(:title) || "Default Title" %></title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_include_tag "application", defer: true %> </head> <body> <%= yield %> </body> </html>
そして、それぞれのビューで:titleを指定したcontent_forを使って、タイトルの値をyield(:title)に渡す。
<% content_for :title, "ブログ詳細" %> <h1><%= @article.title %></h1> <p><%= @article.content %></p>
これによってタイトルを動的に変更できる。
content_forを使わない場合はコントローラでインスタンス変数にタイトルの値を格納してビューから参照する方法があるが、コントローラの責務が増えるので可読性が悪くなる。
javascript_include_tag
javascript_include_tagはJavaScriptファイルを読み込むヘルパー。第一引数にJavaScriptの拡張子を除いたファイル名を指定する。指定するファイルはアセットパイプラインによってビルドされたファイルを参照する。
<%= javascript_include_tag "application" %>
以下のようなHTMLが生成される。
<script src="/assets/application-6472b71b26d30a0e6525e3872d53125ac65db0d91d4217d27b92d9323cefbb16.js"></script>
application.html.erbのheadタグ内にデフォルトで書かれているヘルパーメソッド
Rails7では、デフォルトのapplication.html.erbのheadタグ内には以下のヘルパーが記述されている。
csrf_meta_tagscsp_meta_tagstylesheet_link_tagjavascript_importmap_tags
csrf_meta_tags
csrf_meta_tagsは、CSRF対策のためのメタタグを生成するヘルパー。これによってフォーム送信時にCSRFトークンが自動的に挿入される。
<%= csrf_meta_tags %>
以下のHTMLが生成される。
<meta name="csrf-param" content="authenticity_token"> <meta name="csrf-token" content="aO6hwQZepeRLfUd_qG0TeKTaK00Yqxb8Z1jaHLpImmCSvBjp4takTNt13sebmseZ2tV-s9SCVfVko-YaTovb3A">
csp_meta_tag
csp_meta_tagは、クロスサイトスクリプティング攻撃を緩和するContent Security Policy(CSP)の設定を指定するメタタグを生成するヘルパー。これによってサイトのセキュリティが強化される。
<%= csp_meta_tag %>
stylesheet_link_tag
stylesheet_link_tagはスタイルシートのリンクタグを生成して、CSSファイルを読み込むヘルパー。第一引数にCSSやSCSSの拡張子を除いたファイル名を指定する。
指定するファイルはアセットパイプラインによってビルドされたファイルを参照する。
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
Rails7ではTurboがデフォルトになっているので、"data-turbo-track": "reload"が指定されている。Turboはデフォルトで初回しかアセットを読み込まないようになっているので、"data-turbo-track": "reload"によって変更があった場合にもアセットを読み込むようにする。
以下のHTMLが生成される。
<link rel="stylesheet" href="/assets/application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css" data-turbo-track="reload">
javascript_importmap_tags
javascript_importmap_tagsは、JavaScriptのImportmapを設定するためのヘルパー。
<%= javascript_importmap_tags %>
以下のHTMLが生成される。
<script type="importmap" data-turbo-track="reload">{ "imports": { "application": "/assets/application-6472b71b26d30a0e6525e3872d53125ac65db0d91d4217d27b92d9323cefbb16.js", "@hotwired/turbo-rails": "/assets/turbo.min-dfd93b3092d1d0ff56557294538d069bdbb28977d3987cb39bc0dd892f32fc57.js" } }</script>
type="importmap"でスクリプトがImportmapを定義することを示している。
"imports": {}の部分は、モジュールの名前と対応するURLを指定している。