Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby on Rails: 今後標準で使えるようになるかもしれないViewComponentを試してみる

ViewComponentとは?

3rd Party製のコンポーネントフレームワークのためにレンダー済みのHTMLを返すrender_inが定義されているObjectをActionView:: Helpers::RenderingHelper#renderに渡せるようになりました。

github.com

上記対応は当初ViewComponentの前準備としてあげられたPRだったので、Rails 6.1で標準になるのでは?と期待があったのですが、

techracho.bpsinc.jp

特に入らず現状はGitHub社がViewComponentというgemとして公開しています。

github.com

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.org

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みたいなのが実現出来るかなぁとか妄想したのですが、

vanillaice000.blog.fc2.com

実際にコードに起こすと以下のような感じになったのですが、

  • inlineでCSSを書いてしまうと、stylelint、autoprefixer等の既存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の値をちゃんと見れくれたりとかゆいところに手が届いてる感じが良いですね✨

公式ドキュメントには、他にもいろいろな機能が記載されているのでぜひ気になる方は見てみてください📚

viewcomponent.org