Injecting form options and input tags from the model

111 views Asked by At

How do I add options and fields to my form generated by a builder with form_for @user in the model? (ie. without touching the HTML)

The reason I want to do that is that I am adding a pluggable module to my model, and I want it to automatically (a) add a data attribute in the HTML to provide a hook for the Javascript, and (b) add an extra field in the form.

For example adding such a module to my model:

module Dataable
  def form_options
    { 'data-foo' => true }
  end

  def form_builder_extra_fields
    hidden_field_tag :the_data
  end
end

User.send :include, Dataable

would make form_for to output:

<form {...} data-foo>
  <input type="hidden" name="user[the_data]" {...} />
  {...}
</form>

in the view.

Of course those methods I've just made up. The question is thus two-fold; how to add (1) form options and (2) form tags dynamically in the model.

I am in the process of prying form_for right now, but I wonder if anybody knew.

2

There are 2 answers

1
Andrew Wei On

I'm not sure I fully understand what you're trying to do. If all you want to do carry some info from the controller to the form, so that it's submitted later, and using the User as a passenger.

Controller

@user.new
@user.whatever = 'stuff'

View

= form_for @user do |f|
  = hidden_tag 'user[whatever]', value: @user.whatever

If you're repeating it over and over again, and want it in the model

Model

def add_data
  self.whatever = 'stuff'
end

Controller

@user.new
@user.add_data()
0
Jonathan Allard On

Short answer: form_for is a helper, and the rational thing to do would be to override it with a helper. I'm unsure if I'm at the right level of abstraction, but for now, it's what seems to work.

The helper will call acts_like? to determine if the module has been included.

module UsersHelper
  def form_for_users(options = {}, &block)
    if @user.acts_like? :dataable
      options.deep_merge!({ html: { 'data-foo' => true } })

      new_block = Proc.new do |builder|
        content = capture(builder, &block)

        output = ActiveSupport::SafeBuffer.new
        output.safe_concat(tag :input, {type: 'hidden', name: 'user[the_data]'})
        output << content
      end
    else
      new_block = block
    end

    form_for(@user, options, &new_block)
  end
end

Happy monkeypatching!