Rendering twice in custom FormBuilder

I’m trying to implement a custom form builder with a builder function that outputs both a hidden_field and then renders a ViewComponent:

class MyFormBuilder < ActionView::Helpers::FormBuilder
  def icon_picker(method)
     hidden_field(method)
     @template.render MyViewComponent.new(method)
  end
end

When I do this however, only the last line is rendered when calling icon_picker (the view component - the hidden_tag is not outputted. Indeed if I do the following:

def icon_picker(method)
   hidden_field(method)
   text_field(method)
end

Only the text field is output. I’m not quite sure why calling @template.render multiple times inside a form builder function only outputs the last thing rendered within the scope, but is anyone able to point me in the right direction to getting this to work?

Thanks

Like any other Ruby method, the last line is implicitly returned. If you want to chain multiple tags together in the return value, you need to either wrap them in another tag within a block, or use safe_merge to combine them into an instance of an ActiveSupport::SafeBuffer.

#inside your method
safe_join([
  one tag,
  another tag # , etc...
])
# now you return a single value
# or...

tag.div do
  hidden_field
  text_field
  # etc...
end
# now you return a single safe tag

Hope this helps,

Walter

Thanks Walter, I think I got myself a bit confused as I was using @template.render and (wrongly) thought this was writing to a template output/buffer. Makes sense that whatever is returned is simply appended to the form.

For anyone else with a similar problem the correct solution following Walter’s feedback is:

def icon_picker(method)
  @template.safe_join [
    hidden_field(method),
    @template.render MyViewComponent.new(method)
  ]
end

Are you sure you don’t need a comma between those two tags? I could swear that’s not optional, although I don’t have an example running in front of me…

Walter

Yep you’re right! Updated my example.

I don’t know view components but with just regular helpers you can also just use + instead of html_join. I.E.

hidden_field(method) + text_field(method)

As long as both things are html safe it will just join them together. safe_join provides other things (join separator, flattening, etc) but if you just need to put two things together + is probably a bit nicer to read.