Adding a simple mail form to a Rails app is well-documented throughout the web. However, there's not as much documentation for a mail form on a single-page app that submits with Ajax. This tutorial will address a quick and straightforward way to put a mail form on a single-page web site.
First let's get an app up an running. I'm using rvm for my versioning, though it's not necessary.
mkdir contactForm cd contactForm mkdir contact_form cd contact_form rvm use ruby-2.4.0 gem install rails rails new .
Make your initial git commit. Rails 5 automatically initializes the repository.
Let's add the gems we'll need to the Gemfile.
gem 'bootstrap-sass', '~> 3.3', '>= 3.3.6' gem 'mail_form' gem 'jquery-rails', '~> 4.1', '>= 4.1.1' gem 'dotenv-rails', groups: [:development, :test]
bundle install
To make development more secure we're using the dotenv gem; add .env to the .gitignore file. Now make the .env file in the root directory. In that file add your email credentials; something like this. You will be protected from erroneously uploading sensitive credentials to GitHub, which can cause a huge headache .
GMAIL_USERNAME=your_user_name GMAIL_PASSWORD=your_gmail_password
Make sure your app/assets/javascripts/application.js file looks something like this. We'll need the jQuery for the Ajax rendering.
//= require jquery //= require jquery_ujs //= require turbolinks //= require bootstrap //= require_tree .
Start or restart the rails server.
Now let's set up a basic, very basic, one-page app with an index page which will be our home page and can be easily customized. The following code in the Rails console will generate the Home controller with an index action.
rails g controller Home index
Go to app/views/home/index.html.erb and replace the existing boilerplate with this code.
<h1>This is my one page app!</h1> <div id="contact"> <%= render 'contact_form' %> </div>
In the app/views/home folder make a _contact_form.html.erb partial and insert the following code. Notice the remote: true option in the form method helper which enables Ajax in Rails.
<%= form_for @contact, url: home_index_path, remote: true do |f| %> <div class="col-md-6"> <%= f.label :name %></br> <%= f.text_field :name, required: true, class: "contact-form-text-area" %></br> <%= f.label :email %></br> <%= f.text_field :email, required: true, class: "contact-form-text-area" %></br> <%= f.label :message %></br> <%= f.text_area :message, rows: 8, cols: 40, required: true, class: "contact-form-text-area", placeholder: "Send a message to Glenn."%></br> <div class= "hidden"> <%= f.label :nickname %> <%= f.text_field :nickname, :hint => 'Leave this field blank!' %> </div> <%= f.submit 'Send Message', class: 'btn btn-primary' %> </div> <% end %> <div class="col-md-6" id="flash-message"> <%= render 'flash' %> </div>
Create another partial, _flash.html.erb, in the home folder which is going to hold our code for the flash messages. Insert the following into that file.
<% flash.each do |message_type, message| %> <%= content_tag(:div, message, class: "alert alert-#{message_type}") %> <% end %>
Again in the same home folder, make a create.js.erb file which will be called with the create action in the controller.
// Test for ajax success console.log("This is the create.js.erb file"); // Render flash message $('#contact').html("<%= j render 'contact_form' %>"); $('#flash-message').html("<%= j render 'flash' %>").delay(3000).fadeOut(4000);
Go to the config/routes.rb file and add the following to set up the routes and the actions for the home page that will contain the mail form.
root 'home#index' resources :home, only: [:index, :new, :create]
In the home_controller.rb file replace what's there with the following.
class HomeController < ApplicationController def index @contact = Home.new(params[:home]) end def create @contact = Home.new(params[:home]) @contact.request = request respond_to do |format| if @contact.deliver # re-initialize Home object for cleared form @contact = Home.new format.html { render 'index'} format.js { flash.now[:success] = @message = "Thank you for your message. I'll get back to you soon!" } else format.html { render 'index' } format.js { flash.now[:error] = @message = "Message did not send." } end end end end
The index action will render our one-page app and the create action will handle the creation and submission of the mail form. Now let's set up our model. In the models folder make a home.rb file and add the following code substituting your email credentials in the headers method. Since we are not storing our form data in a database, the model does not inherit from ActiveRecord::Base
class Home < MailForm::Base attribute :name, :validate => true attribute :email, :validate => /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i attribute :message attribute :nickname, :captcha => true # Declare the e-mail headers. It accepts anything the mail method # in ActionMailer accepts. def headers { :subject => "Contact You", :to => "your_email@your_domain.com", :from => %("#{name}" <#{email}>) } end end
Let's add some basic styling to our home.scss file.
@import "bootstrap-sprockets"; @import "bootstrap"; body { background-color: black; color: white; } .contact-form-text-area { color: black; } .hidden { display: none; } .alert-error { background-color: #f2dede; border-color: #eed3d7; color: #b94a48; text-align: left; font-size: .8em; } .alert-alert { background-color: #f2dede; border-color: #eed3d7; color: #b94a48; text-align: left; font-size: .8em; } .alert-success { background-color: #dff0d8; border-color: #d6e9c6; color: #468847; text-align: center; font-size: .8em; } .alert-notice { background-color: #dff0d8; border-color: #d6e9c6; color: #468847; text-align: left; font-size: .8em; }
At localhost:3000 we should see our nice mail form.
rails s
Now we'll configure our Rails app to actually send a message. In the config/environments/development.rb file add the following code. Enter the user_name and password credentials exactly as noted as they are imported via the .dotenv gem. NOTE: If using Google mail you'll probably need to reduce the security settings on your Google mail account for your development Rails app to send a message from your development server.
# For mail_form gem config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors= true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, domain: 'gmail.com', user_name: ENV["GMAIL_USERNAME"], password: ENV["GMAIL_PASSWORD"], authentication: 'plain', enable_starttls_auto: true }
Now if you go back to your home page and fill in a test message, your form will send.
Success! You now have a mail form in Rails.