How can I DRY up conditional content_for in Rails view? - ruby-on-rails

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

Related

Create multiple objects on a button click in Rails

I have template_sections and sections and on a button click in my view I want
a) get all template_sections with a specific template_id, send them to the sections_controller
or
b) the section_controller method transform should get the template_sections there
and
iterate over them and create a new section object for each one of them, where I would set is_template = 0 and template_id = nil and save them.
View:
<div class="col-2 deploy-template">
<%= button_to 'Deploy Template', transform_sections_path, method: :post, *{call controller's method or send objects from here to transform method}*, remote: true, type: 'button', class: "btn btn-secondary btn-sm ml-0 mr-4" %>
</div>
Controller:
def new
#section = Section.new
if params[:is_template]
date = (Time.at(params['day'].to_i*86400-1)).strftime("%Y-%m-%d")
#section.starts_at_datetime = (date + ' ' + params['time']).in_time_zone rescue nil
else
#section.starts_at_datetime = (params['date'] + ' ' + params['time']).in_time_zone rescue nil
end
respond_to do |format|
format.js
end
end
def create
#section = Section.new(section_params)
#section.account_id = session_account_id
#section.is_template = true if params[:section][:is_template] == 'true'
#section.template_id = params[:section][:template_id]
respond_to do |format|
if #section.save
format.js
flash.now[:success] = "saved successfuly"
create_event #section, "create", "Create"
else
flash.now[:danger] = "#{#section.errors.full_messages}"
format.js { render 'layouts/notifications' }
end
end
end
def transform
template_sections.each do |template_section|
#save each template_section with is_template = 0 and template_id = nil
end
What you can do is pass your template sections to your controller through the params hash like so:
<%= button_to 'Deploy Template', method: :post, transform_sections_path(template_sections: [YOUR ARRAY VARIABLE HERE]), remote: true, type: 'button', class: "btn btn-secondary btn-sm ml-0 mr-4" %>
Your template sections should then be available through params[:template_sections] inside of your controller for you to do whatever you feel like doing, (I am assuming post: template_sections_path points to the transform action in your controller).
def transform
template_sections = params[:template_sections]
template_sections.each do |template_section|
#save each template_section with is_template = 0 and template_id = nil
end
I don't know what your TemplateSection model looks like so I will refrain from writing controller code that won't work, but hopefully once you have the data in your controller you will be able to work through it, and if not I am happy to help with a little bit more information.
As an additional note, "method: :post", and "type: button" seem redundant when using a button_to tag as those are the defaults, you can remove them unless you have a specific reason for having them.

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 - 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

DRY view components in Rails views

I want to create abstract components for my views, that don't disclose the way they will be rendered.
The example is an tabbed nav box like the tabs from bootstrap
In my view I want to write s.th. like:
= tab_section(self) do
- tab 'tab1' do
%p tab1 content
= link_to example_var, "#top"
- tab 'tab2' do
%p tab2 content
= link_to 'http://example.com', "#top"
which should then be rendered to s.th. like this:
<ul>
<li>tab1</li>
<li>tab2</li>
</ul>
<div class='content'>
<div class='tab'>
<p>tab1 content</p>
<a href='#top'>this could also be an #var from the controller</a>
</div>
<div class='tab'>
<p>tab2 content</p>
<a href='#top'>http://example.com</a>
</div>
</div>
All my attempts to defer rendering of the tab 'content' failed. I created a minimal rails app demonstrating the three approaches I took.
Take a look at the application_helper.rb and the welcome#show view.
What is the correct way to do such thing?
I got some support and found the following solution:
The outer 'component' must be passed into the block to call the inner function upon:
= tab_section2 do |section|
- section.tab 'tab1' do
%p tab1 content
= link_to example_var, "#top"
- section.tab 'tab2' do
%p tab2 content
= link_to 'http://example.com', "#top"
Since we don't need to bind the block to the tab_section instance (previously done with instance_exec), we can yield the block directly.
def tab_section2(&blk)
raise "Y U NO SUPPLY block?" unless block_given?
ts = TabSection2.new(self, self)
yield(ts)
render :partial => '/tab2', locals: {section: ts}
end
The partial renders the output of the tabs render function:
%ul
- section.tabs.each do |tab|
%li= tab.name
.content
- section.tabs.each do |tab|
.tab
= tab.render.html_safe
Which is implemented as follows:
class Tab2
attr_reader :name, :blk
def initialize(name, context, &blk)
#context = context
#name = name
#blk = blk
end
def render
#context.capture_haml(&#blk)
end
end
class TabSection2
attr_reader :tabs
def initialize(context)
#context = context
#tabs = []
end
def tab(name, &blk)
raise "Y U NO SUPPLY block?" unless block_given?
#tabs << Tab2.new(name, #context, &blk)
end
end

highlight div or p after loading

Hello i need to highlight a div after my page is loading, i don't know if i should use a partial or something like that, the partial also has to have 2 variables disponible.
Code to highlight after loading by 5 seconds:
<div id="total_de_venta">
<% if #instalador.a_destajo? %>
<div align="right"><b>Total de la venta: $<%= #venta.precio_de_venta + #venta.precio_de_instalacion*2 %></b></div>
<% else %>
<div align="right"><b>Total de la venta: $<%= #venta.precio_de_venta + #venta.precio_de_instalacion*2 %></b></div>
<% end %>
</div>
controller :
def show
#venta = Venta.find(params[:id])
#este_instalador_id = #venta.instalador_id
#instalador = Instalador.find_by_id(#este_instalador_id)
#visual_effect(:highlight, "total_zone", :duration => 3.0)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #project }
end
end
Can't you just do it by some Javascript only construction? I'm not a ruby programmer, but I'm sure you can add a 'onLoad' event to your page. The 'onLoad' event fires after the page is loaded. In the 'onLoad' handler you can add your desired effect, e.g. :
Effect.Highlight($('id_of_element'), [ your options]);
To add more than one 'onLoad' handler and execute them all, you can use the following Prototype function:
Event.observe(window, 'load', yourJavascriptFunction);

Resources