🦔

RailsとWebComponentsが組み合わされる事例紹介

2024/08/09に公開

もともとRailsを使っていたバックエンド中心のエンジニアで、あるときWebComponentsに興味があり、色々調べていたら、意外とRailsやその関連するものでWebComponentsが利用されていることがわかりました。

弊社でのWebComponentsの利用事例を交えながら、
意外とRailsとWebComponentsは組み合わせて使われているのだなということを知ってもらえると幸いです。

RailsのTubroはそもそもWebComponentsでできている

Turbo Frames Turbo StereamsでWebComponentsは利用されています。
RailsHelperではTurbo Framesは下記のように設定されますが、実態としては下記が出力されます。

<!-- Rails Helper -->
<%= turbo_frame_tag 'message_id' do %>
    <h1>My message title</h1>
    <p>My message content</p>
    <a href="/messages/1/edit">Edit this message</a>
<% end %> 

<!-- html -->

<turbo-frame id="message_1">
    <h1>My message title</h1>
    <p>My message content</p>
    <a href="/messages/1/edit">Edit this message</a>
</turbo-frame>
また、Turbo Streamsはブロックで囲んだ内容が `<template>`タグ囲まれます。

``` html
<!-- erb -->
<%= turbo_stream.update "followers" do %>
  <div id="followers">
    <%= @user.followers.count %>
  </div>
<% end %>


<turbo-stream action="update" target="followers">
  <template>
    <div id="followers">
      This div will be appended to the element with the DOM ID "messages".
    </div>
  </template>
</turbo-stream>

後は実際にTurbo Frames, Turbo Streamsのコード見ると一目瞭然ですね。

https://github.com/hotwired/turbo/blob/main/src/elements/stream_element.js#L28

https://github.com/hotwired/turbo/blob/main/src/elements/frame_element.js#L22

GitHub Elements

GitHubでもRailsとTurbo、WebComponentsは活用していて、
GitHub Elementsというリポジトリがあり、GitHubや関連サービスでも利用されているようです。
OSSになっているのでよければこちらもご覧ください。

https://github.com/github/github-elements

弊プロダクトでどのように使っているか

そんな感じで、フロントエンド中心のWeb開発に懐疑的な意見を持つ、RailsでもWebComponentsを活用してフロントエンドスタックが実装されていることがわかりました。
私たちのプロダクトでは、Stimulus.jsの代わりにWebComponentsを活用しています。
この時、具体的にどのようにしてWebComponentsを使ったコンポーネントを作成しているのかを記載していきます。

ちょっとした動きを実現するWebComponentsを作成する

例えば、セレクトボックスに対して下記のような簡単なWebComponentsを定義して、
query-type属性に定義されたパラメータをつけてページ遷移を行うようにしています。
このような利用方法はStimulusの代替となる利用方法かなと思っています。

export class OrderSelector extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.queryType = this.getAttribute('query-type');
    this.addEventListener('change', this.onChange.bind(this));
  }

  onChange(e) {
    const changedValue = e.target.value;
    const url = new URL(window.location);
    const params = new URLSearchParams(url.search);
    params.set(this.queryType, changedValue);

    window.location.replace(url);
  }
}
<order-selector query-type="order">
    <% = select_tag :order, options_for_select(@order_types, params[:order]) %>
</order-selector>

無限スクロール

一覧画面で利用するような簡単な仕様の無限スクロールもWebComponentsで実装しました。
これはDHHがgistsでStimulusで実装したものをWebComponentsで再実装したものです。

Stimulus
https://gist.github.com/dhh/f459dfc3455d2376ce3a7ecb026e6fdf

WebComponents
https://gist.github.com/webuilder240/5993e7f01f941dc5a3d5c5471bd69669

turbo-railsのようにHelperで使いやすく

WebComponentsで実装されているので、erbにタグをそのまま書いてもいいのですが、
Turbo StreamsのようにWebComponentsの内部で template タグを使ったりしていて、
ついつい template タグを書きそびれてしまったりということもあるので、
下記のようにRails Helperを実装しておき、Railsからも利用しやすいようにしています。

def menu_modal_content(class_name:, &block)
  tag.menu_modal_content(class: class_name, data: {stop: stop}) do
    tag.template do
      yield
    end
  end
end
<%= menu_modal_content do %>
    <li>
        <%= link_to '編集する', edit_blog_path(blog), target: "_blank" %>
    </li>
    <li>
        <%= link_to '非公開にする', draft_blog_path(blog), method: :post %>
    </li>
<% end %>

まとめ

このように身近にWebComponentsはデザインシステムやUIの共通化意外にも存在価値があって、
意外にもRailsや周辺プロダクトで活用されていることがわかりました。
他にもRailsとWebComponentsの活用事例がありましたら教えてください!

OSIRO テックブログ

Discussion