🦓

[Rails]turboによる非同期インライン編集

2023/09/24に公開

はじめに

インライン編集は、ページ上の特定の要素(タイトルなど)をクリックすると、その要素が編集モード(フォーム)に切り替わり、テキストを直接編集できるようになります。
フォーム全体の読み込みと送信より素早くにデータを更新でき、UXの向上につながります。

Image from Gyazo

環境

Rails 7.0.7
ruby 3.2.1

tl;dr

ProjectのMVCを作成されていることを前提で進めていきます。

  1. turbo_frameを用意する
  2. フォーム内に切り替わる要素を同じidで指定する
  3. 編集、送信、キャンセルボタンを用意する
  4. コントローラーでturboのレスポンスを有効する
  5. 非同期更新用パーシャルを作成する
  6. 編集、送信、キャンセルボタンを非表示にする
  7. 再利用性を向上する

turbo_frameを用意する

フォームを送信できるようにform_withヘルパータグを使います。
編集ボタンを追加します。

app/views/projects/_project.html.erb
<% frame_id = dom_id(project, "title") %>
<%= form_with model: project, data: { turbo_frame: frame_id } do |form| %>
      <%= turbo_frame_tag frame_id do %>
          <%= project.title %>
          <%= link_to '編集', edit_polymorphic_path(project) %>
       <% end %>
<% end %>

フォーム内に切り替わる要素を同じidで指定する

タイトルをクリックしたらフォームに切り替えたいので、タイトルとフォームに同じidをつけます。
また、送信ボタンとキャンセルボタンも用意します。

app/views/projects/_form.html.erb
<% frame_id = dom_id(@project, "title") %>
<%= turbo_frame_tag frame_id do %>
    <%= form.text_field :title%>
    <%= form.submit %>
    <%= link_to 'キャンセル','' %>
<% end %>

コントローラーでturboのレスポンスを有効する

app/controllers/projects_controller.rb
class ProjectsController < ApplciationController
  def update
    respond_to do |format|
      if @project.update(project_params)
        format.turbo_stream
      else
...
      end
    end
  end
end

非同期更新用パーシャルを作成する

タイトルを更新されたら元の表示に戻したいのでパーシャルを作成します。

app/views/projects/update.turbo_stream.erb
<% frame_id = dom_id(project, "title") %>
<%= form_with model: project, data: { turbo_frame: frame_id } do |form| %>
      <%= turbo_frame_tag frame_id do %>
          <%= project.title %>
          <%= link_to 'edit', edit_polymorphic_path(project) %>
      <% end %>
<% end %>

編集、送信、キャンセルボタンを非表示にする

投稿全体を編集する場合、タイトルの横にあるボタン達をCSSで非表示にしましょう。

.inline-btn {
 display: none;
}
 
.inline-edit .inline-btn {
  display: initial;
}

再利用性を向上する

他の要素にもインライン編集を効かせた方が良いので、_inline_edit.html.erbパーシャルを作成します。
属性をattributeに渡します。

app/views/projects/_inline_edit.html.erb
<% frame_id = dom_id(model, "#{attribute}") %>
<%= form_with model: model, data: { turbo_frame: frame_id } do |form| %>
  <%= turbo_frame_tag frame_id, class: 'inline-edit' do %>
    <%= yield %>
    <%= link_to '編集', edit_polymorphic_path(model) %>
  <% end %>
<% end %>

パーシャルを読み込みます。

app/views/projects/_project.html.erb
<%= render 'projects/inline_edits', model: project, attribute: :title do %>
   <%= project.title %>
<% end %>

フォーム内のturbo_frameもパーシャル化します。

app/views/projects/_inline_fields.html.erb
<% frame_id = dom_id(form.object, "#{attribute}") %>
<%= turbo_frame_tag frame_id do %>
  <%= yield %>
  <%= form.submit %>
  <%= link_to 'キャンセル','' %>
</div>
<% end %>

フォームに読み込みます。

app/views/projects/_form.html.erb
<%= render 'projects/inline_fields', form: form, attribute: :title do%>
   <%= project.title %>
<% end %>

非同期更新用のパーシャルにも反映させます。

app/views/projects/update.turbo_stream.erb
<% frame_id = dom_id(@project, "title") %>
<%= turbo_stream.replace frame_id do%>
  <%= render 'projects/inline_edits', model: @project, attribute: :title do %>
     <%= project.title %>
  <% end %>
<% end %>

終わりに

他のサイトでよく見かけるインライン編集を実装できて良かったです!

参考したリポジトリ
https://github.com/thoughtbot/hotwire-example-template

Discussion