I am attempting the following
A HAML template with
#whitepanelMID
#groups_view_scroller
= render 'show' do
= render 'short_field', locals: {label: 'Name:', value: #group.name}
= render 'short_field', locals: {label: 'Description:', value: #group.description}
And the partials are _show.html.haml (note the use of yield)
%table#vert_table.no_borders{ cellpadding: '0', cellspacing: '0'}
%tbody
%tr
%td{ cols: 2 } My table
= yield
and _short_field.html.haml
%tr
%th.vert_table_heads= label
%td= value
The issue is that yield does not seem to work.
What's the proper way to use block in render in HAML?
Update
I found a workaround, which I don't like.
In the HAML template capture the output like
#whitepanelMID
#groups_view_scroller
- rows = capture_haml do
= render partial: 'short_field', locals: {field_label: 'Name:', value: #group.name}
= render partial: 'short_field', locals: {field_label: 'Site:', value: #group.site.description}
= render partial: 'show', locals:{ content: rows}
%br/
with the modified partial _show.html.haml with a content variable instead of yield
%table#vert_table.no_borders{ cellpadding: '0', cellspacing: '0'}
%tbody
%tr
%td{ cols: 2 } My table
!= content
Happy to hear a better approach!
Just because the link provided by #vidaca is ERB, I want to post an equivalent for HAML.
Use layout: when using the wrapper template
#whitepanelMID
#groups_view_scroller
= render layout: 'show', locals:{ table_title: 'My table'}
= render partial: 'short_field', locals: {field_label: 'Name:', value: #group.name}
= render partial: 'short_field', locals: {field_label: 'Site:', value: #group.site.desccription }
%br/
and _show.html.haml partial (wrapper) like
%table#vert_table.no_borders{ cellpadding: '0', cellspacing: '0'}
%tbody
%tr
%td{ cols: 2 }= table_title
= yield
the wrapped partials (short_field in this case) work as-is.
Hope helps someone.
Related
I need to deal with the front-end of my application only. I understand how to write the templates but, for some reason, a few specific elements jump out of the hierarchy I've created:
%html
%head
%meta{ content: 'text/html; charset=UTF-8', 'http-equiv': 'Content-Type' }
%title Title
%body{ data: { action: action_name, controller: controller_name.camelcase } }
.page-container
= render 'application/timeout'
%header
= render 'application/cookie_banner'
.container
.row
.logo.col-3
= link_to root_path do
= image_tag 'logo.jpg'
.login.col-9
= render 'application/user_information'
%nav.navbar.navbar-expand.navbar-light
= render 'navigation/by_role'
.content-container.container
.row
%section
.section-container
= render 'application/notifications'
= render 'application/flash'
= yield
= render 'application/footer'
I want to wrap the page in '.page-container', but the footer and content-container.container elements are outside the wrapper:
I need the rendering to follow my element hierarchy.
Question: How do I make a partial (that has to be generic) loop through different variables?
I have a tabbed page where I want to use a partial to avoid duplicating my HTML. The tabs are "Videos" and "Articles". It's the exact same HTML but I want to iterate through #videos for videos and #articles for articles.
The idea was to make the product partial completely generic and then somehow pass in #videos or #articles that I want to iterate through.
Partial: _product.html.slim
.col-md-5
.thumbnail
.thumb.spacer-sm
- if product.image.blank?
iframe allowfullscreen="" frameborder="0" mozallowfullscreen="" src="https://player.vimeo.com/product/#{product.vimeo_id}" webkitallowfullscreen=""
- elsif product.vimeo_id.blank?
= image_tag(product.image.url,
class: 'img img-responsive img-rounded')
.caption
.content-group-sm.media
.media-body
h6.text-semibold.no-margin
= product.name
small.text-muted
| by
= product.user.name
- unless product.price.blank?
h6.text-success.media-right.no-margin-bottom.text-semibold
| $
= product.price
= product.description
.panel-footer.panel-footer-transparent
.heading-elements
ul.list-inline.list-inline-separate.heading-text
li
= link_to 'Delete', product_path(product), method: :delete
li
= link_to 'Edit', edit_product_path(product)
HTML view rendering the partial:
.page-container.spacer-minus
.page-content
.row
.col-md-4
.sidebar-default.sidebar-separate
.sidebar-content
.content-group
.panel.no-border-radius-top
ul.navigation
li.navigation-header Navigation
li.active
a data-toggle="tab" href="#videos"
i.icon-video-camera2
| Videos
li
a data-toggle="tab" href="#articles"
i.icon-graduation
| Articles
.col-md-8
.tab-content
#videos.tab-pane.fade.in.active
.row
- #videos.each do |product|
= render 'shared/product'
#articles.tab-pane.fade
.row
- #articles.each do |product|
= render 'shared/product'
I just need my loop to understand what variable I want to iterate through. I cannot include #video or #article in the partial since that will defeat the purpose of having a generic partial.
With this implementation, I get the error:
undefined local variable or method `product' for #<#<Class:0x007fc58053ae60>:0x007fc58884ea68>
Try this code:
#videos.tab-pane.fade.in.active .row -
#videos.each do |video|
= render 'shared/product', product: video
#articles.tab-pane.fade .row -
#articles.each do |article|
= render 'shared/product', product: article
Hi this is pretty stuff if you want articles to come after the videos then try this.
= render partial: "product", collection: #videos
= render partial: "product", collection: #articles
this will remove the loop from your main view but in your case you are not using locals try this
.row
- #videos.each do |video|
= render 'shared/product', locals: {product: video}
.row
- #articles.each do |article|
= render 'shared/product' , locals: {product: article}
It will work.
I want to dynamically add classes to a container based on the name of the class that called my layout using the render method. Is it possible or do I need to pass it manually from each view?
File: emails/inbox.html.haml
render layout: 'shared/v3/panel' do
// whatever
Rendered HTML:
<div id="inbox" class="panel email_container"></div>
My temp solution is to do:
render layout: 'shared/v3/panel', locals: {class_panel: 'email_container', id_panel: 'inbox'} do
// whatever
But I want to do that dynamically.
Final solution: views/shared/_panel.html.haml
- # You need to use: "render layout", not "render partial" to pass the "do" block.
.container_content{class: "#{controller_name}_container #{local_assigns[:panel_class] ? panel_class : ''}", id: action_name}
- if local_assigns[:title]
.headline= local_assigns[:title]
= yield
- if local_assigns[:footer]
.footer= local_assigns[:footer]
- # Example of use:
-#= render layout: 'shared/v3/panel', locals: {title: 'Place', panel_class: 'my_custom_class'} do
-# - if #activity.place.present?
-# %p
-# = t('place')
-# = #activity.place
So I come across with a problem where I would like to save a partial as an html string and render another partial with that string but I keep getting a double render error
def fruits
fruits = Fruits.all
market_html = "#{render partial: 'market', locals: {fruits: fruits}}"
render partial: 'super_market', locals {market_html: market_html}
end
Do you want render_to_string?
market_html = render_to_string partial: 'market', locals: {fruits: fruits}
This bug has me perplexed.
I am displaying a Widget with the last 5 data points and a form for a new data_point. When I try to render both the form and the display partial it fails with a routing error. If I comment out either partial it works fine. So with only the form partial it works, or with only the display partial it works fine with the other.
Three relevant models/controllers: Widget, DataSet and DataPoint. DataPoints is STI so I have different partials to display the different types that match the class name.
When both render :partial => ... are uncommented it looks as if the data_point being passed to the second partial is the same as the data_point being passed to the first partial. It is all nil with only a data_set_id. I tried renaming the local from :data_point to :dp in case it was a name collision.
View, both partials and the routing error shown at the bottom.
Is there a limitation on passing :locals to partials when you are trying to render more than one partial in a view?
View: show.html.haml (the show action on the controller is empty)
- title "#{#widget.type.underscore.humanize} for #{#widget.data_set.name.to_s.downcase}"
.row
.span6
= render :partial => 'data_points/form', :locals => { :data_set => #widget.data_set, :data_point => #widget.data_set.data_points.build }
%br
- last_5 = #widget.data_set.data_points.last(5)
- if last_5.count > 0
%h3= "Last #{pluralize( last_5.count, 'Entry' )}"
- last_5.each do |dp|
%pre
= dp.inspect
= render :partial => "data_points/#{#widget.data_set.data_type.underscore}", :locals => { :data_set => #widget.data_set, :data_point => dp }
- else
%h3 No data yet!
= form_tag( push_widget_path #widget, :method => 'POST' ) do
= submit_tag( "Push Data", :class=>"btn btn-primary" )
= link_to "Edit your data", data_set_path( #widget.data_set ), :class=>'btn'
.span6
= image_tag "widgets/#{#widget.type.underscore}.png"
%p
Using the
%b= link_to #widget.data_set.name, #widget.data_set
data set.
= render :partial => "widgets/#{#widget[:type].underscore}", :locals => { :widget => #widget } rescue nil
%p
= link_to 'Edit', edit_widget_path(#widget), :class=>'btn'
Form Partial (data_points/_form.html.haml)
%pre
= data_set.inspect
%pre
= data_point.inspect
= form_for [data_set,data_point], :html=>{:class => 'well form-horizontal'} do |f|
-if data_point.errors.any?
#error_explanation
%h2= "#{pluralize(data_point.errors.count, "error")} prohibited this data_point from being saved:"
%ul
- data_point.errors.full_messages.each do |msg|
%li= msg
= render :partial => "data_points/#{data_set.data_type.underscore}_form", :locals => { :f => f, :data_point => data_point, :data_set => data_set }
= f.text_field :created_at, :type=>'datetime'
.form-actions
= f.submit 'Save'
Display Partial (data_points/_multi_value_data_point.html.haml)
.data-point{ :id=>"data-point-#{data_point.id}"}
%span.value
- data_point.data.map do |k,v|
- label = data_set.properties[:data_series][k][:name]
%span.key= "#{label}: "
= v
.meta
= data_point.created_at
%span.edit
= link_to 'Edit', edit_data_set_data_point_path( data_set, data_point )
%span.delete
= link_to 'Destroy', [data_set, data_point], :method => :delete, :data => {:confirm => 'Are you sure?'}
=# link_to 'Edit', '#', id: "edit-data-point-#{data_point.id}"
:javascript
$("#edit-data-point-#{data_point.id}").click( function(e) {
$("#data-point-#{data_point.id}").html( "#{escape_javascript( render :partial => "data_points/multi_value_data_point_form", :locals => { data_set: data_set, data_point: data_point } )}");
return false;
});
Routing Error
Routing Error
No route matches {:action=>"edit", :controller=>"data_points", :data_set_id=>#<MultiValueDataSet id: 4, ... properties removed ... type: "MultiValueDataSet">, :id=>#<DataPoint id: nil, data: {}, data_set_id: 4, created_at: nil, updated_at: nil>}
Try running rake routes for more information on available routes.
The problem stems from the fact that the form is building a new element that isn't saved, and last is returning that unsaved element.
Simple solution, replace .last(5) with .find( :all, order: 'id desc', limit: 5 )