2

rails 6.0.3

I have set up a static mail form using the gem 'mail_form'.

I know that it working correctly because I can send an email from the rails console.

The issue is on the form, when I press submit, it is coming up with a routing error.

ActionController::RoutingError (No route matches [POST] "/"):

home page form

<div class="container">
  <%= form_with model: @contact do |f| %>
    <div class="mb-3">
      <h3><%= f.label :name, class: "label" %></h3>
      <%= f.text_field :name, class: "form-control", placeholder: "Write name in here" %>
      <%= f.text_field :email, class: "form-control", placeholder: "Write email in here" %>
      <%= f.text_field :message, class: "form-control", placeholder: "Write message in here" %>
      <%= f.submit %>
    </div>
  <% end %>
</div>

routes.rb

resources :contact, only: [:create]

contact.rb

class Contact < MailForm::Base
  attribute :name, validate: true
  attribute :email, validate: true
  attribute :message
  def headers
    {
      subject: "My Contact Form",
      to: 'myemail@gmail.com',
      from: %("#{name}" <#{email}>)
    }
  end
end

contact_controller.rb

class ContactController < ApplicationController
  def create
    @contact = Contact.new()
    @contact.name = params[:name]
    @contact.email = params[:email]
    @contact.message = params[:message]
    if @contact.deliver

      render json: {message: "Email sent successfully"}
      redirect_to root_path
    else
      render json: @contact.errors
    end
  end
end

What have I done wrong or what step have I missed?

edited controller

class ContactController < ApplicationController

  def create
    @contact = Contact.new(contact_params)
    if @contact.deliver
      redirect_to root_path
    else
      render json: @contact.errors
    end
  end

  private
  def contact_params
    params.require(:contact).permit(:name, :email, :message)
  end
end

edited form

<div class="container">
  <%= form_for Contact.new, url: contacts_path do |f| %>
    <div class="mb-3">
      <h3><%= f.label :name, class: "label" %></h3>
      <%= f.text_field :name, class: "form-control", placeholder: "Write name in here" %>
      <%= f.text_field :email, class: "form-control", placeholder: "Write email in here" %>
      <%= f.text_field :message, class: "form-control", placeholder: "Write message in here" %>
      <%= f.submit %>
    </div>
  <% end %>
</div>

edited routes.rb

resources :contacts, only: [:create]
Lorenzo_xavier
  • 147
  • 2
  • 11

2 Answers2

2
resources :contacts, only: [:create]

Resources should be plural.

class ContactsController < ApplicationController
  def create
    @contact = Contact.new(contact_params)
    if @contact.deliver
      # You can't both render and redirect 
      render json: { message: "Email sent successfully" }
    else
      render json: @contact.errors
    end
  end

  private
  def contact_params
    params.require(:contact)
          .permit(:name, :email, :message)
  end
end

The parameters are nested in params[:contact][:name], params[:contact][:email] etc. Use strong parameters to whitelist the parameters for mass assignment instead of acting as a human compiler.

max
  • 96,212
  • 14
  • 104
  • 165
  • 1
    Seems like the form is trying to post to root "/" which would suggest `@contact` is not set in the action that loads the home page – engineersmnky Apr 21 '21 at 17:23
  • Thank you. I changed the controller to include strong params and when I got the post to work, I realised I couldn't re_direct too. Thanks for the note on resources being plural too, it makes a lot of sense and well explained – Lorenzo_xavier Apr 21 '21 at 18:14
1

@form cannot automatically resolve a path, I think:

 <%= form_with model: @contact do |f| %>
  • Try adding an explicit url: form_with model: @contact, url: contacts_path
  • Also make sure to make controllers always plural like @max said, if using resource or resources, otherwise sooner or later Rails gets confused from my experience. On the other hand, your contact only has one method, you can just add a manual route for that and don't rely on Rails auto naming magic:
# routes

  post "/contact"

# this should also generate a `contact_path` helper, which you can use in the 
stwienert
  • 3,402
  • 24
  • 28
  • 3
    `form_with` likely cannot infer the path because `@contact` is `nil`. In this case `model` is unnecessary (keyword `model:` defaults to `nil`) and `url` alone would be sufficient. – engineersmnky Apr 21 '21 at 17:27
  • 2
    @engineersmnky good point! Author can also set the @contact variable, or just pass ``form_for Contact.new, url: contact_path``. – stwienert Apr 21 '21 at 17:30
  • 1
    thanks @engineersmnky and @stwienert, I have edited the code in my question to reflect the changes you wrote. i've learnt a lot about ```form_for``` and ```form_with``` today, it's great to have a network of people that have the knowledge to share – Lorenzo_xavier Apr 21 '21 at 18:22
  • This advice here is questionable at best, just fix the underlying issues instead. – max Apr 21 '21 at 18:37