Hotwire Turbo: Edit each attribute inline
Allow user to easily edit an attribute by just clicking on it!
This way you can have fewer full-page redirects; don’t have to always render a full edit form.
5 years ago I would have used gem best_in_place for this, but now it can be easily achieved with turbo_frames!
List attributes that you want to be inline-editable
# app/models/customer.rb
class Customer < ApplicationRecord
INLINE_EDITABLE_ATTRS = [:first_name, :last_name, :dob, :tel, :description]
validates :first_name, presence: true
end
Render each inline-editable field inside a separate turbo_frame
. Redirect to the edit_customer_path
. Pass the attribute in the url params attribute: attribute
# app/views/customers/_customer.html.erb
<div id="<%= dom_id customer %>">
<% Customer::INLINE_EDITABLE_ATTRS.each do |attribute| %>
<%= turbo_frame_tag attribute do %>
<p>
<strong><%= attribute %>:</strong>
<%= link_to (customer[attribute].presence || 'Edit'), [:edit, customer, attribute: attribute] %>
</p>
<% end %>
<% end %>
</div>
Render the form with a dynamicly-set turbo_frame
name. Render only the field with a matching name as params[:attribute]
.
# app/views/customers/_inline_attribute_form.html.erb
<%= turbo_frame_tag params[:attribute] do %>
<%= form_with(model: customer, url: [customer, attribute: params[:attribute]], method: :patch) do |form| %>
<% if customer.errors.any? %>
<div style="color: red">
<ul>
<% customer.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<% if params[:attribute].eql? 'first_name' %>
<div>
<%= form.label :first_name, style: "display: block" %>
<%= form.text_field :first_name, onchange: 'this.form.requestSubmit()' %>
</div>
<% end %>
<% if params[:attribute].eql? 'last_name' %>
<div>
<%= form.label :last_name, style: "display: block" %>
<%= form.text_field :last_name, onchange: 'this.form.requestSubmit()' %>
</div>
<% end %>
<% if params[:attribute].eql? 'dob' %>
<div>
<%= form.label :dob, style: "display: block" %>
<%= form.datetime_field :dob, onchange: 'this.form.requestSubmit()' %>
</div>
<% end %>
<% if params[:attribute].eql? 'tel' %>
<div>
<%= form.label :tel, style: "display: block" %>
<%= form.number_field :tel, onchange: 'this.form.requestSubmit()' %>
</div>
<% end %>
<% if params[:attribute].eql? 'description' %>
<div>
<%= form.label :description, style: "display: block" %>
<%= form.text_area :description, onchange: 'this.form.requestSubmit()' %>
</div>
<% end %>
<div>
<%= form.submit %>
</div>
<% end %>
<% end %>
Finally, you can use both the attribute-edit form and the normal form. Just render the normal fomr if no params[:attribute]
is present:
# app/views/customers/edit.html.erb
<% if params[:attribute].present? %>
<%= render "inline_attribute_form", customer: @customer %>
<% else %>
<%= render "form", customer: @customer %>
<% end %>
That’s it! 🎉🥳🍾
Did you like this article? Did it save you some time?