I am using a bootstrapping framework, that has the following snippet, which I use a lot:
<div class="panel panel-default">
<div class="panel-heading">...</div>
<div class="panel-body">
...
</div>
</div>
(The snippet will be actually bigger, but I want to start small)
So I thought I would build a partial and use it, something like this:
Partial
<div class="panel panel-default">
<div class="panel-heading"><%= title %></div>
<div class="panel-body">
<%= yield %>
</div>
</div>
Use example
<%= render partial: "panel", locals: { title: "Hello" } do %>
Testing
<% end %>
But this apparently is not working. I get the following error:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
Am I doing something wrong here? Did I understood partials wrong?
In order for yield to work I think you need to use render :layout instead of/in addition to :partial:
<%= render layout: "panel", locals: { title: "Hello" } do %>
Testing
<% end %>
Have a read of the PartialRenderer examples for more information
Related
I want to use the same partial, but changing the main variable of the loop inside the partial so that the layout is the same, but different results show in each tab. I thought I could make this happen using locals, but I tried it with no luck. Any ideas?
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="campaigns-active-tab">
<%= render partial: 'campaigns_list' %>
</div>
<div class="tab-pane fade" id="pills-profile" role="tabpanel" aria-labelledby="campaigns-inactive-tab">
<%= render partial: 'campaigns_list' %>
</div>
</div>
Controller:
#active_campaigns = #campaigns.where(status: 1).order(created_at: :desc)
#inactive_campaigns = #campaigns.where(status: 2).order(created_at: :desc)
Partial:
Here, the variable should change from active to inactive campaigns:
<% #campaigns.each do |campaign| %>
<tr scope="row d-flex align-items-center justify-content-center">
I thought I could make this happen with locals
Yes you can; however, the code you provided does not use locals.
Local usage would be:
<%= render partial: 'campaigns_list', locals: { campaigns: #active_campaigns } %>
And you can thus loop through campaigns within the partial.
Docs for locals
I have a Page model that has many TextBlock:
class Page < ApplicationRecord
has_many :text_blocks, as: :textable, dependent: :destroy
accepts_nested_attributes_for :text_blocks
end
In the form where a page can be edited, I have to display 3 text blocks in the upper part of the form, and 3 text blocks in the lower part of the form. Here is the code in my view...
Upper text blocks:
<%= render 'shared/admin/form-fields/text-blocks-form', f: f, number: 3, value: true %>
Lower text blocks:
<%= render 'shared/admin/form-fields/text-blocks-form', f: f, number: 2, value: false %>
And here is the text-blocks-form partial:
<div class="form-group">
<div class="row">
<div class="col-lg-12 text-blocks">
<div class="row">
<% number.times { f.object.text_blocks.where(upper_position: value).build } unless f.object.text_blocks.where(upper_position: value).any? %>
<%= f.fields_for :text_blocks do |input| %>
<div class="<%= "col-lg-#{cells(number)}" %>">
<%= render 'shared/admin/form-fields/text-blocks-fields', f: input, value: value %>
</div>
<% end %>
</div>
</div>
</div>
</div>
Upper text blocks are built as expected, there are 3 blocks but in the lower part of the form, there are 5 blocks instead of 2. It seems, that it just add 3 + 2. Is there any way to build text blocks as it was described above? Thanks ahead.
I can easily explain why you have this problem, maybe a little less easy to fix this in good way.
The reason why have this that in the first run you create three nested items (on the association), and then iterate over them using f.fields_for :text_blocks, and in the second run you add two more, and then iterating over f.fields_for :text_blocks will iterate over all five created blocks.
An easy fix would be to change your view as follows:
<div class="form-group">
<div class="row">
<div class="col-lg-12 text-blocks">
<div class="row">
<% number.times { f.object.text_blocks.where(upper_position: value).build } unless f.object.text_blocks.where(upper_position: value).any? %>
<%= f.fields_for :text_blocks do |input| %>
<%- if input.object.upper_position == value %>
<div class="<%= "col-lg-#{cells(number)}" %>">
<%= render 'shared/admin/form-fields/text-blocks-fields', f: input, value: value %>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
If you always have 5 blocks (3 on top and 2 below), I would also consider adding a position or order column, allowing the textblocks to be re-rendered in the correct position after save as well.
I have a function:
def self.get_component(component_name, properties{})
render :partial => component_name :collection => properties{}
end
That is meant to render a partial from the argument 'component_name' and then pass the array of properties for said component via a collection.
I have a class calling said method with
get_component('members_card/display', properties{member_name => "Jason"})
Which I haven't actually checked yet, but i've been told I don't actually have to explicitly set the data in the array. I finally have the partial:
<div class="members-card">
<div class="container">
<div class="card">
<img class="card__profile" alt="display name" src="">
<div class="card__details">
<h4 class="card__title"> <%= ??? %> </h4>
</div>
</div>
</div>
</div>
But I don't have a clue how to actually call the data in the array i've set as a collection for the partial, so that i could populate the name.
I see things such as #properties or properties.member_name, would one of these be the correct solution, and if so, why?
Use something like:
properties = ['a','b','c']
render partial: 'my_partial', locals: {
my_collection: properties
}
Then in my_partial.html.erb:
<% my_collection.each do |collection| %>
...
<% end %>
On my views I use 1 form that includes a block that renders comments. I do not want to run it when creating a new record. So, I tried conditions like so...
<% unless #annotation_id.nil? %>
<hr>
<div class="row">
<div class="col-md-8">
<h4>Comments</h4>
<%= render #annotation.comments %>
</div>
<div class="col-md-4">
<%= render 'comments/form' %>
</div>
</div>
<% end %>
This however results in never displaying the block - also when the annotation record exists. What am I doing wrong?
You don't show that you have actually set #annotation_id to something.
A simpler way might be to use the .new_record? method instead, like:
<% unless #annotation.new_record? %>
...
<% end %>
use if #annotation.persisted? or unless #annotation.new_record?
In one of my views I apply a layout to a block of code:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
#... code for sign in form here
<% end %>
The layout is a div that has png shadows on all four sides.
Since I use this layout all over my site, I want to pass a variable to the layout that specifies the width of the shadowed div. I tried using content for in the code block:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do %>
<% content_for :box_width %>640<% end %>
#... code for sign in form here
<% end %>
# In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield %>
</div>
</div>
</div>
</div>
This didn't work and instead resulted in a double render of the entire code block.
What's the best way to tackle this problem?
Figured it out.
From the API: "You can also yield multiple times in one layout and use block arguments to differentiate the sections."
Solution:
# In app/views/sessions/new.html.erb
<% render :layout => 'home/shadow_box' do | section | %>
<%- case section when :box_width -%>
#width goes here. I.e., 640px
<%- when :content -%>
#code block goes here
<% end -%>
<% end %>
#In app/views/home/_shadow_box.html.erb
<div class="shadow-one" style="width:<%= yield :box_width %>;">
<div class="corner-a"></div>
<div class="corner-b"></div>
<div class="shadow-two">
<div class="shadow-three">
<div class="shadow-four">
<%= yield :content %>
</div>
</div>
</div>
</div>
First you need to know the difference between layouts and partials. Partials are generally from the view but can also be used from the controller if you are using ajax. Layouts are almost always used in the controller.
First create a file in a shared folder such as application/ and in this folder put a file call it whatever you want but it will contain the material that you want to include all over your site. Then when you pass a variable to a partial it's called in the partial as a local variable. Also with partials you don't need to say render :partial => you just put render 'application/some_file'
So from the view you want this:
<%= render 'application/your_file', :div_size => '600' %>
And then from the partial in the folder such as application/your_file.html.erb do this:
<div style="width:<%= div_width %>px;">
content
</div>