Skip to main content

24 Forms With Multiple Submit Buttons

Episode 34 · December 5, 2014

Learn how to create forms with multiple submit buttons

Forms


Resources

Thanks to Amirol Ahmad for suggesting the topic!

Transcripts

In this episode we're going to talk about creating forms that have multiple submit buttons, so in this case a good example that I came up with, was creating blog posts where you can publish them immediately or you can save them as a draft.

Hopping into our text editor, you can see that we already have the publish button in there, and all we have to do is copy this line and paste it in, and change

<%= f.submit "Publish", class: "btn btn-primary" %>

to:

<%= f.submit "Save as draft", class: "btn btn-default" %>

Now we have these two submit buttons, and we can test the way that the form works by looking in our params inside our Rails logs. Let's create a test post, some text, and we'll click the publish button here to create it, and then we can look in our terminal and see what happened. If you look at the last POST request, you can see that the commit message inside the parameters hash was "Publish", so that means that when you submit these, the button that is submitted is set up so that it gets submitted based upon the text on the button you clicked. So that's really really neat, you can have this next post, click on "Save as draft", and if we look at our terminal and scroll down to that post, that post request now has a commit message of "Save as draft", and it's as simple as that, so inside of your controller, you can look for the params commit and determine if you've saved as draft or published the post.

Inside our posts_controller.rb we can createa couple methods here that are helpers that allow us to add the logic into our create and update actions. I'm going to go down here at the bottom and create a published? method, and this is going to look at the params[:commit] and see if it equals "Publish". Also, I'm not going to use it, but you can add a method save_as_draft for the other one and have that as well, in case you want to do separate logic here in these controllers. There's a handful of ways that you can update these actions to support this. One would be

def create 
    @post = Post.new(post_params)
    @post.published_at = Time.zone.now if publising?

Choosing the names for the methods is important, published and publishing can both be candidates, but remember the importance of readability for yourself and other developers. You can name those accordingly however you want

Another approach might be updating the post once it's saved.

respond_to do |format| 
    if @post.save
        @post.update(published_at: Time.zone.now) if publishing?
        #rest of the code 

I often prefer going something like this, and the reason for that is because if the post gets saved and any of the publish logic fails, the post will automatically be saved as a draft and you will have it around in case the publish action fails. So maybe you have something like when you publish the post, rather then just simply setting the published_at: date, maybe you have a publish method on your post and inside of there it saves it, sets the published_at time, but it also goes and tells MailChamp to send out an email to everybody saying that we just published this new post. You know, you don't want that to fail here, so could do something like this where if it failed, it would be ok because you wouldn’t lose your progress. So I'm just going to go back to setting the published_at attribute here right after we create a new one in memory, and in our update we can also add that same thing down here, because when you're editing a post, you still want to be able to publish it if you saved it as a draft. That is how you can separate your logic and have two different paths of handling your actions based upon which button was clicked in the form.

Let's take a look at this in our browser and see if it's working in the way we want it to. If we clicked "Edit", now we have a functional "Publish" and "Save as Draft" buttons, and if we save as draft, it should still stay a draft, and if we publish this, the time stamp for the "published at" time is set and it marks the post as published. Now when we edit again, it's a little odd though, because now we have a publish, and it's already published, and we have a "Save as draft", but if you save as draft it still publishes and it didn't reset the post to a draft, so we definitely need to update the formm and if our post is published, let's add a couple other actions here, otherwise, we will have the regular publish and save as draft

app/views/posts/_form.html.erb

<div class="form-group">
    <% if @post.published? %>
        <%= f.submit "Update", class: "btn btn-primary" %>
        <%= f.submit "Unpublish", class: "btn btn-default" %> 
    <% else %> 
        <%= f.submit "Publish", class: "btn btn-primary" %>
        <%= f.submit "Save as Draft", class: "btn btn-default" %>
</div> 

If we change that, we need to go into our controller and do some modifications as well, to give meaning to the "Unpublish" text

app/controllers/posts_controller.rb

 def update 
    @post.published_at = Time.zone.now if publishing?
    @post.published_at = nil if unpublishing?
 #Rest of code
 end 
 #Some more code
 def unpublishing? 
    params[:commit] == "Unpublish" 
 end 

And this is only going to affect the update action, obviously you can refactor this however you like and reorganize it, whatever makes more sense for you, but in this example, we now have the update and unpublish, successfully resetting that time stamp there, that is something that you'll probably need to do most of the time, each time the buttons change, and that's actually a really really good thing for usability, for you to update these buttons accordingly.

Hopping back to the form before we wrap up, I've talked about in a previous episode about using the button tag here instead of submit, to create HTML inside of those buttons, so you could have loading animations magically with that data attribute that we talked about in a previous episode, which I definitely recommend checking out if you haven't seen it before.

Now the problem with this is when you do a submit tag,

<input type="submit" name="commit" value="Unpublish">
<%= f.submit "Unpublish", class: "btn btn-primary" %> 

you create this tag with the bottom one, and that's what's produced, however when you use a button tag, you simply get basically this button, you get "Update" inside of it, and close the tag, so the benefit here is you can put HTML inside, the problem is that you're missing all of the important information (name="commit" value="Unpublish"), you don't need the type of course, but you do need to put the name as commit and the value and match that to the text that you want, so you can change these values with the value option here, and if you want to use those buttons still, you can say name=commit and value=(whatever string you want), so that should produce the correct name and value and submit those when you click those buttons. I almost always anymore use the button tag now, because I can add the loading animations to it and etcetera. But if you're fine using the submit tag, that's really easy and that's the way to go. And the other thing to mention before we leave is that if you make a JavaScript form out of this, you're going to run into a little bit of trouble, so this is basically going to have the jQuery UJS library, take a look at this form when you click any of these submit buttons, it's going to serialize the form, and that means that it's going to look at every single one of these, and then submit that value and create a JSON hash and submit that over basically, so that is going to cause some problems, because it will serialize all of these at the same time, because it doesn't really know how to do that separately, so you're going to have to write some JavaScript to handle this accordingly, but it's really not to bad, so maybe you have a hidden field that you'll update with the action, so maybe you're using the button tags, and then when you click on one of these buttons, you will have JavaScript intercept it, submit a hidden field with that same name or something like that. So that's up to you to dive into if you're interested, and you'll probably also cover it in a future episode but not in this one.

That wraps up how to create forms with multiple buttons, and of course, as anything goes, you start simply and think you just need two buttons, but then pretty quickly you need four and so on. I hope you found this interesting, it certainly makes life a lot better for your users because it's a really fantastic way to add a little bit extra usability into your applications

Transcript written by Miguel

Discussion