Search code examples
ruby-on-railsrubytwitter-bootstrapcontent-tag

Content_tag block nested inside content_tag block - Form Builder


I am trying to create a rails FormBuilder to style a form using bootstrap styling:

<div class="form-group has-error">
  <label for="exampleInputEmail1">Email address</label>
  <div class="input-group>
    <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
    <span class="input-group-addon glyphicon glyphicon-user"></span>
  </div>
  <p class="help-block>can't be blank</p>
</div>

I was able to get the form builder to work when I only had one div tag(excluded the input-group for the traditional bootstrap form. My problem is that I can't get the nested content_tag with the div with class "input-group" to work properly. I have tried adding the elements and wrapping the content_tag in a capture to no avail.

class LargeFormBuilder < ActionView::Helpers::FormBuilder
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::CaptureHelper
  include ActionView::Helpers::TextHelper

  attr_accessor :output_buffer

  %w(text_field text_area email_field password_field).each do |form_method|
    define_method(form_method) do |*args|
      attribute = args[0]
      options = args[1] || {}
      options[:label] ||= attribute
      options[:class] ||= "form-control input-lg"
      label_text ||= options.delete(:label).to_s.titleize
      content_tag(:div, class: "form-group #{'has-error' if !object.errors[attribute].empty?}") do
        concat label(attribute, label_text, class: "control-label")
        concat (
            content_tag(:div, class: "input-group") do
              concat super(attribute, options)
              concat content_tag(:span, "", class: "input-group-addon glyphicon glyphicon-user")
            end
         )
        concat errors_for_field(attribute)
      end
    end
  end

  def errors_for_field(attribute, options={})
    return "" if object.errors[attribute].empty?
    content_tag(:p, object.errors[attribute].to_sentence.capitalize, class: "help-block")
  end

end

Solution

  • It's much simpler that that in fact you do not need to use concat you can just use + to achieve the same result in a clearer way, just be sure that the first string is html_safe

    content_tag(:div, class: "form-group #{'has-error' if !object.errors[attribute].empty?}") do
      label(attribute, label_text, class: "control-label").html_safe + \
      content_tag(:div, class: "input-group") do
        concat super(attribute, options)
        concat content_tag(:span, "", class: "input-group-addon glyphicon glyphicon-user")
      end + \
      errors_for_field(attribute)
    end
    

    and for even more readability

    content_tag(:div, class: "form-group #{'has-error' if !object.errors[attribute].empty?}") do
      label_html = label(attribute, label_text, class: "control-label")
    
      input_html = content_tag(:div, class: "input-group") do
        concat super(attribute, options)
        concat content_tag(:span, "", class: "input-group-addon glyphicon glyphicon-user")
      end
    
      error_html = errors_for_field(attribute)
    
      label_html.html_safe + input_html + error_html
    end