Slim Partial with Yield - ruby-on-rails

I have a Slim partial for a widget. The widget has common elements and then some custom content that I want to yield to. What's the correct syntax for that? Here's what I thought would work, but doesn't.
Widget Partial
.container
.title= title
.content
== yield
Page
.page
= render partial: "widget_partial", locals: { title: "Content 1" } do
div Some really awesome content.
= render partial: "widget_partial", locals: { title: "Content 2" } do
span Different but also awesome content.

According to the Action View Overview on partials (section 3.2.3), when you are only using a partial template and locals, you can get away with a minimal approach like this:
(widget partial)
.container
.title
p #{title}
= yield
(Page)
= render "widget_partial", title: "content 1" do
.div
p Some really awesome content.
= render "widget_partial", title: "content 2" do
.div
p Different but also awesome content.
Note that in the partial, itself, you cannot put text directly into a
div, so I added a <p> element.

You should add layout argument in the render. Something like this:
(widget partial)
.container
.title = title
.content
= yield
(page)
.page
= render layout: 'widget_partial', locals: { title: 'Content 1' } do
div Some really awesome content.
= render layout: 'widget_partial', locals: { title: 'Content 2' } do
span Different but also awesome content.
Doc: http://edgeguides.rubyonrails.org/action_view_overview.html#partial-layouts

Related

How to render HTML hierarchy correctly

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.

Rails Locals - Passing Connected Model Data

My model structure is pretty solid. I have MarketingDeliverySystem has_many MarketingSections. MarketingSections has_many MarketingVideos.
I have another segment: GroupDevelopment has_many GroupSections. GroupSections has_many GroupVideos.
I'm trying to use a partial to pass the variables, thus DRYing it all up.
I have the following that I'm trying to pass to the partial:
= render partial: '/sales_presentations/sales_presentation',
locals: { marketing_delivery_system: #marketing_delivery_system,
first_video: first_marketing_video(#marketing_delivery_system),
sales_presentation: #marketing_delivery_system}
Then in the partial I have the following:
.rounded-box-header.blue-bg #{sales_presentation.title}
ul
- sales_presentation.sections.ordered.each_with_index do |section, index|
- list_class = 'section show'
- list_class = 'section hide' if index != 0
li
= link_to section.title, '#', class: 'section', data: { id: section.id }
ul class="#{list_class}" data-section-id="#{section.id}"
- section.videos.ordered.each do |video|
li.video
= link_to video.title, '#',
class: 'video video-link',
data: { video: video.youtube_link,
sales_presentation: sales_presentation.title.parameterize }
.seven.columns
.row
div id="#{sales_presentation.title.parameterize}-container"
video {
id="#{sales_presentation.title.parameterize}-video-player"
class="video-js vjs-default-skin videos"
height=400
poster=""
controls preload='none'
data-default-url="#{first_video(sales_presentation)&.youtube_link}"
I previously had issues with sales_presentation.title at the top until I updated the locals.
My question/issue is how do I pass in through the locals to use for sales_presentation.sections instead to use #marketing_delivery_system.marketing.sections?
I thought I could just put that in through locals:
sales_presentation.sections: #marketing_delivery_system.marketing_sections but I end up with a massive syntax error.
I've also tried creating a partial view for these two and then changed sales_presentation throughout the view to mod. Then changed mod.sections to mod_section and setting that in the locals to mod_section: #marketing_delivery_system.marketing_section. The problem then gets into that I end up needing to hit video later in the iteration. So then that has the same issue.
You misunderstand the meaning of locals in partials.
Says we have
<%= render partial: 'image', locals: {size: #image.size, extension: #image.extension} %>
It means that in image partial now we can use local variable size and extension (keys) as #image.size and #image.extension (values).
Put in locals: {} all local variables you want.
So you can't write in locals sales_presentation.sections: #marketing_delivery_system.marketing.sections
But you can sales_presentation_sections: #marketing_delivery_system.marketing.section
Also you have problem with this code:
locals: { marketing_delivery_system: #marketing_delivery_system,
first_video: first_marketing_video(#marketing_delivery_system),
sales_presentation: #marketing_delivery_system }
marketing_delivery_system and sales_presentation will be with the same value.

How can I DRY up conditional content_for in Rails view?

contests/show.html.haml:
- if modal
- content_for(:modal_header) do
= render 'contests/contest_header'
- content_for(:modal_body) do
= render 'contests/contest_body'
- else
= render 'contests/contest_header'
= render 'contests/contest_body'
modal is a boolean local which indicates whether the view is being rendered in a modal template, vs the regular application template. The modal has content_for blocks while the main application template does not:
layouts/modal.html.haml:
-modal = true
.modal-header
.row
.col-xs-11
= yield(:modal_header)
.col-xs-1
%button{'type': 'button', 'class': 'close', 'data-dismiss': 'modal', 'aria-label': 'close'}
%span{'aria-hidden': 'true'} ×
.modal-body
= render 'layouts/messages'
- if content_for?(:modal_body)
= yield(:modal_body)
- else
=yield
What I'm looking for is something that can conditionally make the content_for blocks disable when modal is false. Rather than repeating my render statements.
Found a solution from another SO answer! https://stackoverflow.com/a/27398070/378622
Here's my implementation
# app/helpers/application_helper.rb
def modal_content_for(name, &block)
if params[:modal] == '1'
content_for 'modal_' + name.to_s, nil, &block
else
capture_haml(&block)
end
end

Rails - Know caller filename when using render layout

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

Override body attribute in HAML

I am currently on a Rails project and I want to override the attributes defined in the body tag in a view that is not part of the layout.
Like this:
app/views/layouts/application.haml
...header
%body
= render 'shared/menu'
.container
= yield
In another view. I want the body tag to have certain attributes:
app/views/stuff/index.haml
{data: {spy: 'scroll', target: '.d-sidebar', :'twttr-rendered' => 'true'}} << these attributes should apper in the body tag of the layout file. They should only be visible on the sub page
What is the easiest way to achieve this?
You could probably provide it:
in layout
%body{ yield(:body_attributes) }
in view
- provide(:body_attributes, { class: '', data: {} })

Resources