deeply nested content_tag, concat and rails 3

12,885

You don't need to ever use concat.

Each Rails helper returns a string with some html in it:

tag(:br) # "<br>"

So your simplest helper method would be this:

# "<br>"
def br
  tag(:br)
end

If you have multiple strings of html just sum them up:

# "<button>Close</button><button>Save</button>"
def modal_buttons
  content_tag(:button, "Close") + content_tag(:button, "Save")
end

Note that you can't just call them as they don't modify the view

# "<button>Save</button>"
def modal_buttons
  content_tag(:button, "Close") # this won't do anything
  content_tag(:button, "Save")
end

For blocks the same rules apply:

# "<div><button>Close</button><button>Save</button></div>"
def modal_footer
  content_tag(:div) do
    # what block returns will be inside the div
    content_tag(:button, "Close") + content_tag(:button, "Save")
  end
end

def modal_body
  content_tag(:div) do
    modal_header + yield + modal_footer
  end
end

As a side note, using just Rails helpers to construct the whole view isn't really their intended purpose. They are supposed to help you in dynamic places, static html is better done in ERB templates.

Share:
12,885
Michael K Madison
Author by

Michael K Madison

Updated on June 15, 2022

Comments

  • Michael K Madison
    Michael K Madison almost 2 years

    I'm frustrated using Rails 3.2 and making a helper for Bootstrap modals. I don't understand when you need concat versus when you don't sometimes I end up with tags missing and sometimes I end up with a hash with all the options between before and ending tags. When I use concat on any content-tag with a do-end all hell breaks loose. All I want to do is replicate this html:

    <div id="stupid_modal" class="modal hide fade" tabindex="-1" data-width="760">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fontello-icon-cancel-1"></i></button>
            <h4>Modal header</h4>
        </div>
        <div class="modal-body">
            <div class="page-header">
                <p>Test header 1 2 3.</p>
            </div>
            <div class="row-fluid">
    
               content here... blah blah
    
            </div>
        </div>
        <div class="modal-footer">
            <button type="button" data-dismiss="modal" class="btn">Close</button>
            <button type="button" class="btn btn-green">Save changes</button>
        </div>
    </div>
    

    I cannot for the life of me get the button in the h4 in the modal header to work out right. Neither can I get the page header to appear in the modal body.

    My helper looks like this:

    module ModalHelper
    def modal(css_id, header_text, hidden = true, options = {},&block)
        class_text = "modal"
        class_text += " hide fade" if hidden
        content_tag(:div, :class => 'modal hide fade', :id => css_id, :style => ("display:none;" if hidden)) do 
            concat modal_header(header_text)
            concat modal_body(&block)
            concat modal_footer
        end
    end
    
    def modal_button(link_text, href)
        modal_caller link_text, href, :button
    end
    
    def modal_link(link_text, href)
        modal_caller link_text, href
    end
    
    private
    
    def modal_caller(link_text, href, type = nil)
        options = { :"data-toggle" => "modal" }
        options.merge!({ :class => "btn" }) if type == :button
        link_to link_text, "#" + href, options
    end
    
    def modal_header(header_text)
        content_tag(:div, :class => 'modal-header') do
            concat content_tag(:button,(content_tag(:i, :class => 'fontello-icon-cancel-1')),:class => 'close', :"data-dismiss" => 'modal', :"aria-hidden" => 'true')
            concat content_tag(:h4, header_text)
        end
    end
    
    def modal_body(page_header = "")
        content_tag(:div, :class => 'modal-body') do
            content_tag(:div, :class => 'page-header') do
                concat content_tag(:p, page_header)
            end
            content_tag(:div, :class => 'row-fluid') do
                yield
            end
        end     
    end 
    
    def modal_footer
        content_tag(:div, :class => 'modal-footer') do
            concat content_tag(:button, 'Close', type: "button", :class => 'btn btn-boo', :"data-dismiss" => 'modal')
            concat content_tag(:button, 'Save', type: "button", class: 'btn btn-green')
        end     
    end 
    

    end

    And the link looks like this:

    <%= modal_link "New Stupid Modal", "stupid_modal" %>
    

    And the modal html looks like this:

    <%= modal('stupid_modal', 'Shouldnt this work?', submit: true, tabindex: '-1') do %>
        <% render 'stupid_modal_partials/stupid_modal' %>
    <% end %>
    

    The output is this:

    <button aria-hidden="true" class="close" data-dismiss="modal"><i>{:class=&gt;&quot;fontello-icon-cancel-1&quot;}</i></button>
    

    which looks like this in the page source:

    <i>{:class=>"fontello-icon-cancel-1"}</i>
    

    Update:

    changing modal_header to this works:

    def modal_header(header_text)
        content_tag(:div, :class => 'modal-header') do
            concat content_tag(:button,(content_tag(:i, "",:class => 'fontello-icon-cancel-1')),:class => 'close', :"data-dismiss" => 'modal', :"aria-hidden" => 'true')
            concat content_tag(:h4, header_text)
        end
    end
    

    But this doesn't:

    def modal_header(header_text)
        content_tag(:div, :class => 'modal-header') do
            concat content_tag(:button,:class => 'close', :"data-dismiss" => 'modal', :"aria-hidden" => 'true') do
                concat content_tag(:i, "",:class => 'fontello-icon-cancel-1')
            end
            concat content_tag(:h4, header_text)
        end
    end
    

    which begs the question, wzup with concat? and am i missing something -- I also tried empty quotes as the second argument to the button content_tag