Warm tip: This article is reproduced from stackoverflow.com, please click
ruby ruby-on-rails

Namespaced Form Objects in Rails

发布于 2020-04-03 23:41:29

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!

Questioner
Awire
Viewed
76
max 2020-01-31 20:47

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.