Suppose we wanted to modify a basic Rails application so there was the option of a preview before creating a new record.
What is a good pattern for adding a second button to a form?
There are two options:
- One controller action with a flag: nice articles by EyeDeal and Myers Development (from 2008 and 2006, respectively, but still accurate)
- Two controller actions where there are two form buttons that each post to a separate action
I like having two controller actions, since it feels to me like different behavior should live in different actions. The complete code is posted on github, which creates a new_preview action, slightly modifies the new action so it can be called with parameters, and makes corresponding view changes.
Here’s what I did to create the app (on the command line):
rails notes cd notes ruby script/generate scaffold note title:string content:text
Then I edited app/controllers/notes_controller.rb:
def new_preview @note = Note.new(params[:note]) end
Then I edited app/views/notes/new.html.rb to add a button that triggers the new action (see whole file):
<%= f.submit 'Preview', :onclick => "this.form.action='new_preview'"%>
And then I created the new_preview template file (app/views/notes/new_preview.html.erb) which I made look just like the show page with a hidden form:
<p> <b>Title:</b> <%=h @note.title %> </p> <p> <b>Description:</b> <%=h @note.description %> </p> <% form_for(@note) do |f| %> <%= f.hidden_field :title, :value => @note.title %> <%= f.hidden_field :description, :value => @note.description %> <p> <%= f.submit 'Re-Edit', :onclick => "this.form.action='new'" %> <%= f.submit 'Create' %> </p> <% end %>
and finally, I edited the ‘new’ action so that it could accept parameters to fill in the form (to handle the case of “Re-Edit”)
def new @note = Note.new(params[:note]) respond_to do |format| format.html # new.html.erb format.xml { render :xml => @note } end end
Overall I like this solution. There is a clear separation of code between preview and create.
However….
There was some interesting discussion via twitter about this. I must admit that using onclick like that feels a little hack-ish. Greg Moeck offered an alternate methodology, which seemed like a lot of code to solve a simple problem. Elad Meidar argued that my technique was obtrusive. I wonder… seems to me that the whole idea behind unobtrusive Javascript is to separate behavior from structure. But we put behavior in ERB files all the time, like “link_to ‘Edit’, edit_note_path(note)” or even “form_for(@note)” … I wonder if the trick would be :onclick => “this.form.action=’#{new_note_path}’”
Interested in other people’s thoughts, suggestions, code snippets…
3 Comments
Your point about it being in a partial to begin with is well taken. It wouldn’t really be a DRY issue. However that “hackish” feeling still just doesn’t sit right with me. The unobtrusive solution I offered above would only have to be written once, and it would work for many different places, not just the preview action. It has a bit more code but it just feels better to me, and it may work out to be less if you end up implementing the sameish functionality elsewhere, pointing at a different action.
What do you think about splitting in the controller according to params[:commit]?
params[:commit] tells you the title of the button that was clicked.
So invoke make-changes if button-title is ‘Re-Edit’ and invoke create if it is ‘Create’. Make those two constants in the controller, and use the constants in the view.
Stephan
Hi Stephan,
I think your approach is similar to the ones I reference I the top of the article (“1. One controller action with a flag”). It seems like a fine approach. My preference is to have two controller actions, since I think it is cleaner for each controller action to do only one thing.
Best,
Sarah
One Trackback
[...] This post was mentioned on Twitter by Sarah Allen. Sarah Allen said: rails: adding a preview option to new form http://bit.ly/9qaE3C — thanks @britt @gregmoeck @eladmeidar @MikeG1 @lenary [...]