I'm having a hard time trying to get setup some custom form objects in a new Rails 6 project I am building. I suspect this may be due to namespacing issues but I can't yet tell for sure.
app/views/saasy/signups/new.html.erb
<%= form_with(model: [ :saasy, @signup ], url: saasy_signups_path(@signup), local: true) do |signup_form| %>
<%= fields_for :account, @signup.account do |account_fields| %>
Organization name: <%= account_fields.text_field :organization %>
<% end %>
<%= signup_form.submit %>
<% end %>
app/controllers/saasy/signups_controller.rb
class Saasy::SignupsController < ApplicationController
def new
@signup = Saasy::SignupForm.new
end
def create
@signup = Saasy::SignupForm.new(signup_form_params)
@signup.register
end
private
def signup_form_params
params
.require(:saasy_signup_form)
.permit(account_attributes: [:organization])
end
end
config/routes.rb
Rails.application.routes.draw do
namespace :saasy do
resources :signups, only: [:new, :create]
end
end
app/forms/saasy/signup_form.rb
module Saasy
class SignupForm
include ActiveModel::Model
attr_accessor :user, :account
delegate :attributes=, to: :user, prefix: true
delegate :attributes=, to: :account, prefix: true
def initialize(params= {})
super(params)
@user = Saasy::User.new(params)
@account = Saasy::Account.new(params)
end
def register
# eventually do actual signup stuff here
end
end
end
However, whenever I test it I get back the following message: param is missing or the value is empty: signup_form
The params hash looks like this:
{
"authenticity_token"=>"BhhvRaYKf220afExocQ//LIY1jszVsXs+lThFeUFKvr6ciVBsa+22mSxwO3yT6mK2uOsWSCKx9gL6WIaGmmvSg==",
"account"=>{"organization"=>"Example Name"},
"commit"=>"Create Signup form"
}
I've tried a whole lot of general messing around solutions like playing with the form_with
in the view and changing route names etc but I've not had any luck so far. Any advice would be really appreciated!
This doesn't have anything to do with namespaces. You're just requiring the wrong param key.
def signup_form_params
params
.require(:signup_form)
.permit(account_attributes: [:organization])
end
Rails gets the "key" for the inputs by calling model_name.param_key
on the model that you pass. The param key does not take into account the module nesting of the class. Neither should it really as thats an implementation detail and not nessicarily part of the "public api" that your app exposes via HTTP. Your code organization and the routes / http parameters of your application are two very different things.
You can override the key by providing the scope:
option to form_with
.
<%= form_with(model: [ :saasy, @signup ], scope: :saasy_signup_form, local: true) do |signup_form| %>
<%= fields_for :account, @signup.account do |account_fields| %>
Organization name: <%= account_fields.text_field :organization %>
<% end %>
<%= signup_form.submit %>
<% end %>
But IMHO its just silly.