If your app has a business model that often is created in sequence (think tasks or products), the Save and add another UX paradigm can be a great option.
I stumbled upon a great example in the Linear app.
Previously articles often show a solution using two buttons. Then based on the value of the commit key (i.e. the button’s value) in the params, determine which branch is needed within the create
action of the controller.
{
// …
"product": {
"name": "Example Product",
"description": "This is an example product",
"price": "9.99"
},
"commit": "Save and Add Another",
"controller": "products",
"action": "create"
}
Given above params within your controller’s action you can do something like this:
# …
if params[:commit] == "Save and Add Another"
# Redirect to the new action
redirect_to new_product_path, notice: "Product created successfully, add another."
else
redirect_to products_path, notice: "Product created successfully."
end
# …
Technically the “checkbox” solution (from Linear) works the same, but instead of checking the button’s value, let’s check for a truthy value of a checkbox. I prefer this UX over giving the user two buttons as it’s cleaner, but also allows to persist the “add another checkbox” using Rails’ session storage.
Let’s create a form partial first:
# app/views/products/new.html.erb
<%= form_with model: @product do |form| %>
<%= form.label :name %>
<%= form.text_field :name %>
<%= form.label :description %>
<%= form.text_field :description %>
<%= form.label :price %>
<%= form.number_field :price %>
<%= form.check_box :add_another, { checked: session[:add_another] } %>
<%= form.label :add_another, "Add another after saving" %>
<%= form.submit "Save" %>
<% end %>
See how the session is checked for a add_another
key. It’s set in the controller’s action. Let’s look at it now.
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
@product = Product.new
session[:add_another] ||= false
end
def create
@product = Product.new(product_params)
session[:add_another] = params[:product][:add_another] == "1"
if @product.save
if session[:add_another]
redirect_to new_product_path, notice: "Product added, you can add another."
else
redirect_to products_path, notice: "Product created successfully."
end
else
render :new, status: :unprocessable_entity
end
end
# …
end
The add_another
value is stored in the session and then checked in both the new
action, to toggle the checkbox to either true or false and then to set the value in the session in the create
action.
Of course redirecting to the new page is not the most graceful option and you might show the product form in a modal. If that’s the case, check out this article on how to use Turbo Streams to replace a modal with a new form.
You could do whatever else you want to do using Turbo-Streams: replace the form and so on.
And that’s how easy it is to get this UX in your Rails app.