Rails Hotwire: mastering techniques for streamlining inline editing in your user interface
Tags: webdev, programming, rails, hotwire
Published: 2023-01-12 20:01:02

_Originally posted on [ahmednadar.com](https://www.ahmednadar.com/posts/rails-hotwire-mastering-techniques-for-streamlining-inline-editing-in-your-user-interface)_

-------

 Recently, the inline editing capabilities demand has increased a lot. It allows users to edit certain fields in a form directly on the form page, without navigating to a separate edit page. No refresh nor reload page is needed.  
And why not, since it enhances user experience and saves time.  
  
In the Rails world, thanks to [Hotwire’s stack](https://hotwired.dev/), inline editing is fun and a simple task. In this post, you will know how to implement inline editing in a Rails application using Hotwire Turbo.  
  
## scenario  
  
A client asks OR your application requires you to create a Contact object with `first_name`, `last_name`, and `email` attributes. Users are allowed to inline edit `first_name` and `last_name` attributes only, without a page reload ‘refresh’.  
  
First, we scaffold the `Contact` object like so: `rails g scaffold contacts first_name last_name email`.  
In the `Contact` model, create an `EDITABLE_ATTRIBUTES` constant that lists the attributes to be edited inline `:first_name` and `:last_name`. While you are here, add a validation that requires the `first_name` and `last_name` attributes to be present.  
  
```  
# app/models/contact.rb  
class Contact < ApplicationRecord  
	EDITABLE_ATTRIBUTES = [:first_name, :last_name].freeze  
	validates :first_name, :last_name presence: true  
end  
```  
  
Scaffold kindly enough, generated `_contact.html.erb` partial to render a single contact.  
  
```  
# app/views/contacts/_contact.html.erb  
<div id="<%= dom_id contact %>">  
	<% Contact::EDITABLE_ATTRIBUTES.each do |attribute| %>  
		<%= render "editable_attribute", contact: contact, attribute: attribute %>  
	<% end %>  
</div>  
```  
Above, instead of listing each attribute individually, loop through `EDITABLE_ATTRIBUTES` and render `_editable_attribute.html.erb` partial to edit each attribute.  
  
```  
# app/views/contacts/_editable_attribute.html.erb  
<%= turbo_frame_tag attribute do %>  
	<%= link_to (contact[attribute].presence || 'Edit field'), [:edit, contact, attribute: attribute] %>  
<% end %>  
```  
  
Notice, the use the `turbo_frame_tag` helper here to wrap each attribute with the `id` `attribute`, which matches the same `id` within the `_editable_attribute_form.html.erb` partial below, and updates it using TurboStreams.  
  
```  
# app/views/contacts/_editable_attribute_form.html.erb  
<%= turbo_frame_tag params[:attribute] do %>  
	<%= form_with(model: contact, url: [contact, attribute: attribute], method: :patch) do |form| %>  
		<% if contact.errors.any? %>  
			<div style="color: red">  
				<ul>  
					<% contact.errors.each do |error| %>  
						<li><%= error.full_message %></li>  
					<% end %>  
				</ul>  
			</div>  
		<% end %>  
		<%= form.text_field attribute, onchange: 'this.form.requestSubmit()' %>  
		<%= form.submit %>  
	<% end %>  
<% end %>  
```  
  
Above partial renders a form for editing a single attribute. It uses the `form_with` helper to create a form that submits a `:patch` request to update `contact`. Notice it is using the same id `attribute` that matches the previous partial.  
The form includes a `text_field` field for a dynamically selected attribute matching `name` as `params[:attribute]`. Each field has an `onchange` event that submits the form when the value of the field changes.  
  
Lastly, update the `edit.html.erb` template to render the edit page for contact. If the attribute parameter is present in the request, the template renders the `‌_editable_attribute_form.html.erb` partial to allow the user to edit a single attribute inline. If the attribute parameter is not present, the template renders the form partial to allow the user to edit all attributes of the contact object at once. Rails form that you are familiar with.  
  
```  
# app/views/contacts/edit.html.erb  
<% if params[:attribute].present? %>  
	<%= render "editable_attribute_form", contact: @contact %>  
<% else %>  
	<%= render "form", contact: @contact %>  
<% end %>  
```  
  
As you see. Rails 7 with Hotwire stack offers the amazing ability for developers to do a fantastic job with little effort and no relying on external libraries.  
  
# Refactor  
As all developers do, we like to write less and reduce [DRY](http://www.ahmednadar.com/posts/eliminate-n-1-queries-and-improve-rails-app-performance-with-this-effective-eager-loading-tip). You can simplify and refactor your code as follow.  
  
Extracted the logic for rendering `_editable_attribute.html.erb` and `_editable_attribute_form. HTML.erb` partials code into a new helper module `EditableAttributesHelper`.  
  
By doing so, you need to update both `_contact.html.erb` partial, and `edit.html.erb` template and call the new helper instead.  
  
```  
# app/views/contacts/_contact.html.erb  
<div id="<%= dom_id contact %>">  
	<% Contact::EDITABLE_ATTRIBUTES.each do |attribute| %>  
		<%= turbo_frame_tag attribute do %>  
			<%= render_editable_attribute contact, attribute %>  
		<% end %>  
	<% end %>  
</div>  
```  
  
Above we call the `render_editable_attribute` method and pass in the `contact` object and the `attribute` as arguments.  
  
```  
# app/views/contacts/edit.html.erb  
<% if params[:attribute].present? %>  
	<%= turbo_frame_tag params[:attribute] do %>  
		<%= render_editable_attribute_form @contact, params[:attribute] %>  
	<% end %>  
<% else %>  
	<%= render "form", contact: @contact %>  
<% end %>  
```  
  
And in the `edit.html.erb` template, we call the `render_editable_attribute_form` method and pass in the `@contact` object and the `params[:attribute]` as arguments.  
  
Let's check your new helper module.  

```  
# app/helpers/editable_attributes_helper.rb
	module EditableAttributesHelper
	  def render_editable_attribute(contact, attribute)
	    content_tag(:p) do
	      concat link_to(contact[attribute].presence || 'Edit', [:edit, contact, attribute: attribute])
	    end
	  end
	  
		def render_editable_attribute_form(contact, attribute)
		  form_with(model: contact, url: [contact, attribute: attribute], method: :patch) do |form|
		    content = []
		
		    if contact.errors.any?
		      content << content_tag(:div, class: 'flex text-red-500 flex-col bg-pink-100') do
		        content_tag(:h2, "#{pluralize(contact.errors.count, "error")} prohibited this contact from being saved:") +
		        content_tag(:ul) do
		          contact.errors.each do |error|
		            concat content_tag(:li, error.full_message)
		          end
		        end
		      end
		    end
		
		    content << content_tag(:div) do
		      content << form.text_field(attribute, onchange: 'this.form.requestSubmit()', autofocus: true)
		      content << form.submit("Update #{attribute.humanize}")
		    end
		    content.join.html_safe
		  end
		end
	end 
```  
The `render_editable_attribute` method renders a link to the edit action with the attribute parameter set to the current attribute.  The `render_inline_attribute_form` method renders a `div` element with a form containing a `text_field` tag for the selected attribute. 
If there are any errors, they are displayed in a div element with TailwindCSS styles. 
 
Nothing drastically changed from previous partials.  

  By extracting the logic for rendering both partials into a helper class, we have made the code more reusable and easier to maintain. We can now use the `render_editable_attribute ` and `render_editable_attribute_form` methods in any view in our application to render editable inline attributes and forms.
  
**Notice** `content.join.html_safe`. It takes an array of strings, `content`, and joins them into a single string, by calling `join` method on the array `contnet`. Then it marks the resulting string as safe for output in an HTML context, by calling the `html_safe` method on it. The `html_safe` method tells Rails to not escape the string when it's output in an HTML template, which is typically used when you want to output HTML tags.  

# Final thoughts    
  
Implementing inline editing is a powerful feature that can improve the user experience of your application by allowing users to edit data directly on the page, without having to reload the page or navigate to a separate page.  
Thanks to Rails Hotwire Turbo made the process of doing so easy, fun, and straightforward.

And as usual, Happy Coding 😀 💻