Rails View基礎まとめ

Railsのビューの基本的な知識をまとめました。

理解すべきことは以下です。

  • ビューはMVCのViewのことで、画面の表示を担当する
  • Railsのビューではerbかslimというテンプレートエンジンを使う
  • application.html.erbyieldを使うことでレイアウトを共通化できる
  • パーシャルを使うことでビューを使い回すコンポーネントとして作成できる
  • ヘルパーを使うことで柔軟にビューの要素を作成できる

ビューとは

ビューとは、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が不要で、renderyieldを使うときは== 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 %>
<% if object.errors.any? %>
  <div id="error_explanation">
    <h2><%= object.errors.count %> 件のエラーが発生しました</h2>
    <ul>
      <% object.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

このような書き方で、エラーメッセージを共通化できる。

ヘルパーメソッド

ビューではデフォルトで使えるヘルパーメソッドがある。

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タグの中にbuttoninput 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_tagjavascript_include_tagを使ったCSSJavaScriptファイルの生成時にも使われる。

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_tagform_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_tagJavaScriptファイルを読み込むヘルパー。第一引数にJavaScriptの拡張子を除いたファイル名を指定する。指定するファイルはアセットパイプラインによってビルドされたファイルを参照する。

<%= javascript_include_tag "application" %>


以下のようなHTMLが生成される。

<script src="/assets/application-6472b71b26d30a0e6525e3872d53125ac65db0d91d4217d27b92d9323cefbb16.js"></script>

application.html.erbのheadタグ内にデフォルトで書かれているヘルパーメソッド

Rails7では、デフォルトのapplication.html.erbheadタグ内には以下のヘルパーが記述されている。

  • csrf_meta_tags
  • csp_meta_tag
  • stylesheet_link_tag
  • javascript_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スタイルシートのリンクタグを生成して、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を指定している。