ViewComponentとは?
3rd Party製のコンポーネントフレームワークのためにレンダー済みのHTMLを返すrender_in
が定義されているObjectをActionView:: Helpers::RenderingHelper#render
に渡せるようになりました。
上記対応は当初ViewComponentの前準備としてあげられたPRだったので、Rails 6.1で標準になるのでは?と期待があったのですが、
特に入らず現状はGitHub社がViewComponentというgemとして公開しています。
ViewComponentの使い方
Gemfileに以下を追加して、bundle install
を実行します。
gem "view_component", require: "view_component/engine"
以下のコマンドでViewComponentで利用するファイルを作成出来ます。
$ bin/rails generate component Example title
主な生成ファイルと主な用途を以下に記載します。
app/components/example_component.rb
: ロジック、引数をもとにインスタンス変数を生成app/components/example_component.html.erb
: テンプレート、ロジックで生成したインスタンス変数を用いた描画部分を担当
サンプル
app/components/example_component.rb
class ExampleComponent < ViewComponent::Base def initialize(title:) @title = title end end
app/components/example_component.html.erb
<span title="<%= @title %>"><%= @title %></span>
またcall
メソッドにインラインでテンプレートを定義することも出来ます。
class ExampleComponent < ViewComponent::Base def initialize(title:) @title = title end def call ERB.new('<span title="<%= @title %>"><%= @title %></span>').result(binding) end end
※inlineとテンプレートファイルを併用すると、ViewComponent::TemplateError
になるので併用は出来ません。
ViewComponentをテストする
公式ドキュメントはこちら
ViewComponentはbin/rails generate component
時にconfig.generators .test_framework
を参照し、適切なテストファイルを生成してくれます。
今回はRSpecでテストを実行するケースで実行方法をメモしておきます。
以下に記載されているとおりですが、rails_helper
に以下を追記するだけでテストの準備はOKです🙆♂️
require "view_component/test_helpers" RSpec.configure do |config| config.include ViewComponent::TestHelpers, type: :component end
あとは以下のような形でレンダリング結果をテスト出来ます✨
require "rails_helper" RSpec.describe ExampleComponent, type: :component do it "renders component" do result = render_inline(ExampleComponent.new(title: "my title")) expect(result.to_html).to eq <<~HTML <span title="my title">my title</span> HTML end end
おまけ:VueのSFCみたいなのは実現できるか?
個人的には、結論から言うと一部は出来そうですが、厳しそうな感じかなと。。。
HTML5.2からbodyタグの中にstyleタグが記載できるようになったので、VueComponent内で指定したスラグをCSSのセレクタとテンプレートのTOPレベルの要素のclassに指定すれば擬似的にScoped CSSみたいなのが実現出来るかなぁとか妄想したのですが、
実際にコードに起こすと以下のような感じになったのですが、
と結構厳しい感じにですね、、、webpackにvue-component-loaderみたいなのを作ってcssをbuild時に生成するみたいなことをやると、autoprefixerを通すとかはいけたりするんですかね?
# frozen_string_literal: true class ExampleComponent < ViewComponent::Base def initialize(title:, slug:) @title = title @slug = slug.presence || build_slug end def call ERB.new(style+template).result end private attr_reader :title, :slug def template <<~ERB <div class="<%= @slug %>"> <h1><%= @title %></h1> </div> ERB end def style <<~ERB <style> .<%= @slug %> h1 { font-size: 50px; } </style> ERB end def build_slug SecureRandom.urlsafe_base64(8) end end
おわりに
ViewComponent、さすがのGitHub社製ということもあり、configの値をちゃんと見れくれたりとかゆいところに手が届いてる感じが良いですね✨
公式ドキュメントには、他にもいろいろな機能が記載されているのでぜひ気になる方は見てみてください📚