Build a Rails Form (and Skip the Gems!) – Part IV: Form Reset & Success Message

Part IV: Form Reset & Success Message

Overview
Before reading this section, I encourage you to read through the previous posts in this series in case you haven’t already.

At the end of Part III I mentioned that that only two things remain to implement into the contact form: the ability to reset and the ability to display a success message to the end user. I will incorporate this functionality in this final post of this walkthrough series.

Walkthrough

Step 1: Add A Flash Notice to the Message Controller
Open the messages_controller.rb file saved in MyAppName/app/controllers and add a flash[:notice] that contains a success message.

class MessagesController < ApplicationController
def create
@message = Message.new(message_params)
respond_to do |format|
#CREATE NULL ERROR ARRAYS
@errorName = []
@errorContent = []
if @message.save
format.js {flash[:notice] = "Thanks! We've received your message."}
else
format.js
@message.errors.any?
#PUSH ERROR MESSAGES INTO ERROR ARRAYS
if (@message.errors["email"] != nil)
@errorName.push(@message.errors["name"][0])
end
if (@message.errors["content"] != nil)
@errorContent.push(@message.errors["content"][0])
end
end
end
end
 
private
def message_params
params.require(:message).permit(:email, :content)
end
end

Step 2: Add a Success Span to the Form Partial View
Open _form.html.erb saved in MyAppName/app/views/messages and add a span with the Bootstrap .help-block class. This span will contain the success message if and when it is not null.

<%= form_for @message, :html => {:class => "form-horizontal"}, remote: true do |f| %>
<div class="form-group">
<%= f.label :email, "Email address:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.email_field :email, class: "form-control", :placeholder => "Email"%>
<span id="emailBlock" class="help-block"></span>
</div>
</div>
<div class="form-group">
<%= f.label :content, "Message:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.text_area :content, class: "form-control", rows: 5 :placeholder => "Your message"%>
<span id="contentBlock" class="help-block"></span>
</div>
</div>
<%= f.submit "Submit", class: "btn btn-primary", data: {disable_with: "Loading..."} %>
<span id="helpBlock" class="help-block"></span>
<% end %>

Step 3: The JavaScript Partial View
Open the create.js.erb partial view file saved in MyAppName/app/views/messages and add code that inserts success messages into the span added in the previous step.

//REMOVE ANY PREVIOUS NOTICES
$(".notice").remove();
$(".form-group").removeClass("has-error");
 
//DISPLAY ERROR MESSAGES
$("#emailBlock").append('<div class="notice"><%=escape_javascript(@errorEmail[0])%></div>');
$("emailBlock:contains(.)").closest(".form-group").addClass("has-error");
 
$("#contentBlock").append('<div class="notice"><%=escape_javascript(@errorContent[0])%></div>');
$("contentBlock:contains(.)").closest(".form-group").addClass("has-error");
 
//DISPLAY SUCCESS MESSAGE
$("#helpBlock").append('<div class="notice"><%=escape_javascript(flash.discard(:notice))%></div>');

Step 4: Reset the Form – Messages Controller
Open the messages_controller.rb file saved in MyAppName/app/controllers and define a variable that is equal to either 1 or 0 depending on whether the new @message instance was successfully saved.

class MessagesController < ApplicationController
def create
@message = Message.new(message_params)
respond_to do |format|
#CREATE NULL ERROR ARRAYS
@errorName = []
@errorContent = []
if @message.save
format.js {flash[:notice] = "Thanks! We've received your message."}
@resetForm = "1"
else
format.js
@message.errors.any?
#PUSH ERROR MESSAGES INTO ERROR ARRAYS
if (@message.errors["email"] != nil)
@errorName.push(@message.errors["name"][0])
end
if (@message.errors["content"] != nil)
@errorContent.push(@message.errors["content"][0])
end
@resetForm = "0"
end
end
end
 
private
def message_params
params.require(:message).permit(:email, :content)
end
end

Step 5: Reset the form: JavaScript Partial View
Open the create.js.erb partial view file saved in MyAppName/app/views/messages and add the code that resets the form based on the value of the @resetForm variable.

//REMOVE ANY PREVIOUS NOTICES
$(".notice").remove();
$(".form-group").removeClass("has-error");
 
//DISPLAY ERROR MESSAGES
$("#emailBlock").append('<div class="notice"><%=escape_javascript(@errorEmail[0])%></div>');
$("emailBlock:contains(.)").closest(".form-group").addClass("has-error");
 
$("#contentBlock").append('<div class="notice"><%=escape_javascript(@errorContent[0])%></div>');
$("contentBlock:contains(.)").closest(".form-group").addClass("has-error");
 
//DISPLAY SUCCESS MESSAGE
$("#helpBlock").append('<div class="notice"><%=escape_javascript(flash.discard(:notice))%></div>');
 
//RESET FORM IF MESSAGE SAVED
<% if @resetForm == "1" %>
$(".form-horizontal")[0].reset();
<% end %>

To protect user experience, it is important to that the form does not reset following an unsuccessful submission. Isn’t it frustrating when your whole message gets deleted just because you forgot to enter your email address? That doesn’t happen in this form!

Wrap-Up
Now that you’ve reached the end of the walkthrough, go ahead and run $rails server in your computer terminal. The form should have all of the wonderful capabilities mentioned in the walkthrough Overview. The walkthrough Overview also contains all of the final code in on place for your reference. I hope you enjoy your new database-backed Ruby on Rails contact form!

What’s Next?
Now that you know how to build a contact form that stores messages in a database on a local machine, learn about how to configure this form to store messages on the Amazon Web Services global cloud in a DynamoDB database by reading my next post.

 

Did you find this walkthrough helpful?

Do you have questions on the above?

Do you see any errors?

 

If so, please leave your thoughts in the comments below.

Build a Rails Form (and Skip the Gems!) – Part III: Input Validation & Help Text

Part III: Input Validation & Help Text

Overview
Before reading this section, I encourage you to read through the previous posts in this series in case you haven’t already.

At the end of Part II I mentioned that turning on Ajax causes the Messages controller to search for a JavaScript partial view as part of the Ruby on Rails create action. I will populate this view in this post.

However, the form currently accepts any and all submissions (including blank submissions). That’s bad. In this section I will also incorporate validators into the contact form, and I will do this before creating the JavaScript partial view. You’ll see why this order makes sense very soon.

Walkthrough

Step 1: Add Validators and Error Messages to the Message Model
Open the message.rb file saved in MyAppName/app/models and add validators such that:

  1. The database does not accept blank entries
  2. The database does not accept non-email inputs in the email field

class Message < ActiveRecord::Base
validates :email, :presence => {message: "Email address cannot be blank."}, email_format: {message: "Please enter a valid email address."}, :on => :create
validates :content, :presence => {message: "Message content cannot be blank."}, :length => { :minimum => 2, message: "Message is too short." }, :on => :create
end

Mini-Step 1: Install the validates_email_format_of gem
The validates_email_format_of gem allows the email_format validator to work. Though I usually don’t like to use gems, this one is fairly harmless. Install this gem by adding gem 'validates_email_format_of' to the gemfile and running $bundle install from the computer terminal.

Note: It isn’t really necessary to include the email_format validator when you specify in the _form.html.erb view that the email input goes into an email_field, as I did in Part II, Step 1. However, I like to use the email_format validator just in case older browsers do not trigger automatic input validation upon encountering non-email inputs in an email_field.

Step 2: Define Error Message Arrays
Open the messages_controller.rb file saved in MyAppName/app/controllers and define two empty arrays to store error messages. Then, push objects from the default @message.errors hash into one of these two new arrays based on whether the object has either the :email or the :content key.

class MessagesController < ApplicationController
def create
@message = Message.new(message_params)
respond_to do |format|
#CREATE NULL ERROR ARRAYS
@errorName = []
@errorContent = []
if @message.save
format.js
else
format.js
@message.errors.any?
#PUSH ERROR MESSAGES INTO ERROR ARRAYS
if (@message.errors["email"] != nil)
@errorName.push(@message.errors["name"][0])
end
if (@message.errors["content"] != nil)
@errorContent.push(@message.errors["content"][0])
end
end
end
end
 
private
def message_params
params.require(:message).permit(:email, :content)
end
end

Note: There are many different methods for displaying error messages when a user submits inputs that trigger validation errors. I will discuss only one method in this walkthrough. To learn more I encourage you to read about how to Display Error Messages on an Ajax Rails Form: Three Methods.

Step 3: Add Error Spans to the Form Partial View
Open _form.html.erb saved in MyAppName/app/views/messages and add the following spans with the Bootstrap .help-block class:

<%= form_for @message, :html => {:class => "form-horizontal"}, remote: true do |f| %>
<div class="form-group">
<%= f.label :email, "Email address:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.email_field :email, class: "form-control", :placeholder => "Email"%>
<span id="emailBlock" class="help-block"></span>
</div>
</div>
<div class="form-group">
<%= f.label :content, "Message:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.text_area :content, class: "form-control", rows: 5 :placeholder => "Your message"%>
<span id="contentBlock" class="help-block"></span>
</div>
</div>
<%= f.submit "Submit", class: "btn btn-primary", data: {disable_with: "Loading..."} %>
<% end %>

Step 4: The JavaScript Partial View
It’s finally time to create the JavaScript partial view! JavaScript partial views interact with html views (like _form.html.erb). This matters because it means that JavaScript partial views respond to user actions (like form submissions) by changing the objects inside an html view (e.g. by adding content to the error spans added in the previous step) WITHOUT requiring the affected html view to reload. Cool, right?

Open a blank text file and save it as create.js.erb in MyAppName/app/views/messages. Populate this view with the following code:

//REMOVE ANY PREVIOUS NOTICES
$(".notice").remove();
 
//DISPLAY ERROR MESSAGES
$("#emailBlock").append('<div class="notice"><%=escape_javascript(@errorEmail[0])%></div>');
 
$("#contentBlock").append('<div class="notice"><%=escape_javascript(@errorContent[0])%></div>');

Notice the use of escape_javascript? Every time we embed Ruby code into a JavaScript block, we must first escape JavaScript so that Rails will recognize the subsequent code as Ruby and not as JavaScript.

Step 5: Incorporate the Bootstrap .has-error Class
This step is optional, but I really like it. The Bootstrap .has-error class draws attention to incorrect form fields by marking them in red. In the same create.js.erb partial view file, insert the following code to add the .has-error class to .form-groups when form inputs violate the validations:

//REMOVE ANY PREVIOUS NOTICES
$(".notice").remove();
$(".form-group").removeClass("has-error");
 
//DISPLAY ERROR MESSAGES
$("#emailBlock").append('<div class="notice"><%=escape_javascript(@errorEmail[0])%></div>');
$("emailBlock:contains(.)").closest(".form-group").addClass("has-error");
 
$("#contentBlock").append('<div class="notice"><%=escape_javascript(@errorContent[0])%></div>');
$("contentBlock:contains(.)").closest(".form-group").addClass("has-error");

Wrap-Up
At this point in the walkthrough you should have a highly functioning contact form. Go ahead and run $rails server in your computer terminal to see the results so far. Do you notice how the view doesn’t undergo a full-page reload when you press “Submit”? That’s Ajax at work!

Test out the form’s validations by attempting to submit bad inputs. You should see error messages appear upon submitting bad inputs into the form:

red errors

Want proof that bad inputs haven’t made their way into your Rails database? Shut down your rails server and take a look at the Rails database. Not sure how to view the contents of a Rails database? Take a quick read through How to View the Contents of a Rails Database using only the CLI.

There are just two things missing on the form:

  • The ability to reset after submission
  • The ability to display a success message when messages are saved to the database

I will incorporate these two steps in the next, and final, post in this series.

Continue to Part IV: Form Reset & Success Message

 

Did you find this walkthrough helpful?

Do you have questions on the above?

Do you see any errors?

 

If so, please leave your thoughts in the comments below.

Build a Rails Form (and Skip the Gems!) – Part II: All About the Form

Part II: All About the Form

Overview
Before reading this section, I encourage you to read through the previous posts in this series in case you haven’t already.

At the end of Part I I mentioned that in this post I would begin populating the contact form view. In the walkthrough Overview I wrote that the contact form would live inside a partial view contained within the application’s root page (think of a contact form at the bottom of a home page).

Partial views function very well with the built-in Ajax capabilities of Ruby on Rails. Both partial views and Ajax are relevant to this post, and I will do my best to explain how as I walk through the next steps for building a contact form in a Rails application.

Walkthrough

Step 1: Create and Populate the Messages Partial View
Partial views are a lot like regular views in Rails, except they can be contained within other views and manipulated without requiring their containing views to be reloaded. This makes your app faster!

Partial views are differentiated from regular views by the underscore (_) in the beginning of their file names. Open a blank text file, name it _form.html.erb, and save it in MyAppName/app/views/messages.

Then, use the Rails form_for helper to build the contact form infrastructure:

<%= form_for @message, :html => {:class => "form-horizontal"}, remote: true do |f| %>
<div class="form-group">
<%= f.label :email, "Email address:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.email_field :email, class: "form-control", :placeholder => "Email"%>
</div>
</div>
<div class="form-group">
<%= f.label :content, "Message:", class: "col-sm-2 control-label" %>
<div class="col-sm-10">
<%= f.text_area :content, class: "form-control", rows: 5 :placeholder => "Your message"%>
</div>
</div>
<%= f.submit "Submit", class: "btn btn-primary", data: {disable_with: "Loading..."} %>
<% end %>

Note: The Rails form_for helper generates HTML <form></form> tags and also tells browsers which model the form is about (in our case, the Message model). You can learn more about form helpers here.

Note: f.text_field generates a field that would accommodate a single line of text, while f.text_area generates a flexible field that can accommodate multiple lines of text (in this case I specified 5).

Note: data: {disable_with: “Loading…”} disables the Submit button while Rails is communicating with the database and changes the button’s text from “Submit” to “Loading…”. This protects against duplicate submissions.

Note: remote: true turns on Rails’ built-in Ajax capabilities. To learn about the implications of adding remote: true to a form, I encourage you to read A Discussion on Ajax.

An Aside on Bootstrap
The code for the contact form relies heavily on Bootstrap (one of my favorite front-end development tools!). In addition to Bootstrap’s standard grid-based column classes, I also used the following Bootstrap classes in the above code:

  • .form-horizontal for the main form format
  • .form-control for 100% width of textual elements
  • .form-group for optimum spacing of labels and form controls
  • .control-label so that fields can receive validation styles (explained in Part III)
  • .btn .btn-primary for button styling

To make sure that Bootstrap is working properly in your web application, I encourage you to read this post. Also, I’d like to give a shoutout to Rahul Singh for helping me understand how to incorporate Bootstrap into my form. Thanks!

Step 2: Routes
By default, html forms submit HTTP POST requests to a database. Rendering a form that creates a new resource (i.e. a new message) is made possible by an HTTP GET request. HTTP GET and HTTP POST requests are mapped to the Rails new and create actions, respectively. This means that the application router needs a new and a create route for the Message model.

Open the routes.rb file saved in MyAppName/config and insert the following routes:

Rails.application.routes.draw do
root 'pages#index'
get 'messages/new' => 'messages#new'
post 'messages' => 'messages#create'
end

Note: To learn more about how HTTP verbs are mapped to controller actions, click here.

Step 3: Render the Messages Partial View from the Root View
Open the index.html.erb file saved in MyAppName/app/views/pages and render the newly created _form.html.erb partial view.

<div>
<p>Interested in learning more? Send us a message!</p>
<%= render 'messages/form' %>
</div>

Step 4: Define the ‘new’ Action in the Pages Controller
Ruby on Rails tutorials would typically place the new action for the Message model inside the Messages controller, and it would look something like this:

#EXAMPLE ONLY, DON'T USE THIS
class MessagesController < ApplicationController
def new
@message = Message.new
end
end

However, in this walkthrough the new action is called when the _form.html.erb partial view is rendered by the root view. This means that the new action for the Message model must be defined in the Pages controller.

Open the pages_controller.rb file saved in MyAppName/app/controllers and define the new action.

class PagesController < ApplicationController
def index
@message = Message.new
end
end

Step 5: Define the ‘create’ Action in the Messages Controller (Modified for AJAX)
In Step 1 above, I briefly mentioned that including remote: true in the Rails form_for helper turns on the built-in Ajax capabilities of Ruby on Rails. If you understand Ajax, or if you read A Discussion on Ajax, then you will know that Ajax tells the Rails create action to render a JavaScript partial view instead of a default html view. Thus, we need to add a respond_to do |format| block into the Messages controller’s create action that will direct Rails to a JavaScript partial view.

Open the messages_controller.rb file saved in MyAppName/app/controllers and define the create action, along with a message_params method.

class MessagesController < ApplicationController
def create
@message = Message.new(message_params)
respond_to do |format|
if @message.save
format.js
else
format.js
end
end
end
 
private
def message_params
params.require(:message).permit(:email, :content)
end
end

The message_params method is used to safely collect data and update the Rails database. This method requires the model name (Message) and permits the columns that were added to the messages table from the migration file (Part I: Step 7).

Wrap-Up

At this point in the walkthrough the Messages controller renders a JavaScript partial view when the form is submitted. This view doesn’t exist yet but I’ll create it in the next post.

Continue to Part III: Input Validation and Help Text

 

Did you find this walkthrough helpful?

Do you have questions on the above?

Do you see any errors?

 

If so, please leave your thoughts in the comments below.