n.milsht
n.milsht

Reputation: 163

Rails activemodel & actionmailer contact me form returns no route matches [POST]

I'm trying to create a 'contact me' form with activemodel to avoid generating needless tables. When I submit the contact form, rails returns the error No route matches [POST] "/contact/new", despite the following routes

config/routes.rb

resources :contact, only: [:new, :create]

rake routes returns the following...

   contact_index POST   /contact(.:format)                     contact#create
   new_contact GET    /contact/new(.:format)                 contact#new

controller/contact_controller.rb

class ContactController < ApplicationController

  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(params[:contact])
    if @contact.valid?
      ContactMailer.contact_submit(@contact).deliver
      flash[:notice] = "Thank you for your email, I'll respond shortly"
      redirect_to new_contact_path
    else
      render :new
    end
  end
end

mailers/contact_mailer.rb

class ContactMailer < ActionMailer::Base
  default to: ENV[EMAIL_ADDRESS]

  def contact_submit(msg)
    @msg = msg
    mail(from: @msg.email, name: @msg.name, message: @msg.message)
  end
end

models/contact.rb

class Contact
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :name, :email, :message

  validates_format_of :email, :with => /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
  validates_presence_of :message
  validates_presence_of :name

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  def persisted?
    false
  end
end

views/contact/new.html.erb

<%= form_for @contact, url: new_contact_path do |f| %>
  <div class="form-inputs">
    <div class="form-group">
      <%= f.label :name %><br>
      <%= f.text_field :name %>
    </div>
    <div class="form-group">
      <%= f.label :email %><br>
      <%= f.email_field :email %>
    </div>
    <div class="form-group">
      <%= f.label :message %><br>
      <%= f.text_area :message %>
    </div>
  </div>
  <div class="form-actions">
    <%= f.submit %>
  </div>
<% end %>

Upvotes: 0

Views: 94

Answers (2)

Arun Kumar Mohan
Arun Kumar Mohan

Reputation: 11915

You're submitting the form to new_contact_path(/contact/new) whose method is GET and not POST. By default, form_for constructs a form with method set to post.

So, when you submit, rails is looking for new_contact_path with POST verb which doesn't exist and so, no route matches.

Remove the url option from form_for.

<%= form_for @contact do |f| %>
  # form elements
<% end %>

Rails will take care of the url to submit, the form will be submitted to contacts_path(/contacts)

For the above code to work, your route definition should look like the following:

resources :contacts, only: [:new, :create]

Upvotes: 1

max
max

Reputation: 102055

When declaring resources you should use the plural form:

resources :contacts, only: [:new, :create]

This goes with the RESTful idea that you are operating on a collection of resources.

Your form should post to contacts_path not new_contacts_path. The new and edit actions respond to GET and just render forms in rails.

In fact you can just pass the record to form_for and use convention over configuration:

<%= form_for(@contact) %>
  # ...
<% end %>

This will automatically route to to contacts_path. You seldom need to manually set the URL for a form in rails.

Upvotes: 1

Related Questions