Rails use content_for and render the same layout twice - html

I am using a theme that has very specific layouts and I wanted to make a failsafe way to you rails forms.
I have a layout app/views/shared/forms/fields/_layout.html.erb
<div class="js-form-message mb-4">
<div class="js-focus-state input-group u-form">
<div class="input-group g-brd-primary--focus">
<%= yield(:field) %>
</div>
</div>
</div>
And I have two partials.
1-st partial: app/views/shared/forms/fields/_email.html.erb
<% form = locals[:form] %>
<% locals[:required] = locals[:required].nil? ? true : locals[:required] %>
<% locals[:placeholder] = locals[:placeholder] || t('forms.shared.email.placeholder') %>
<%= render layout: "shared/forms/fields/layout", locals: locals do %>
<% content_for(:field) do %>
<%= form.email_field :email,
placeholder: locals[:placeholder],
class: "form-control g-py-15 g-px-15",
"data-error-class"=>"u-has-error-v1-3",
"data-success-class"=>"u-has-success-v1-2",
"data-msg-email" => t('forms.shared.email.validate'),
"data-msg" => t('forms.shared.required'),
autofocus: locals[:autofocus],
required: locals[:required] %>
<% end %>
<% end %>
2-nd partial: app/views/shared/forms/fields/_login.html.erb
<% form = locals[:form] %>
<% locals[:required] = locals[:required].nil? ? true : locals[:required] %>
<% locals[:placeholder] = locals[:placeholder] || t('forms.shared.login.placeholder') %>
<%= render layout: "shared/forms/fields/layout", locals: locals do %>
<% content_for(:field) do %>
<%= form.email_field :login,
placeholder: locals[:placeholder],
class: "form-control g-py-15 g-px-15",
"data-error-class"=>"u-has-error-v1-3",
"data-success-class"=>"u-has-success-v1-2",
"data-msg" => t('forms.shared.required'),
autofocus: locals[:autofocus],
required: locals[:required] %>
<% end %>
<% end %>
And when I do this:
<%= render "shared/forms/fields/email", locals: {form: f} %>
<%= render "shared/forms/fields/login", locals: {form: f} %>
I get
Email Field
Email Field/Login Field
I found out that content_for 'appends' the block that you give it and then when I yield the whole block is returned.
The first time there is nothing in content_for(:field) and it appends to it Email Field. But the second time it does not clear its content and just appends Login Field to it.
I am thinking of adding additional complexity to layout.html.erb so just keeping it inline isn't an option.
Is there a way to tell to the layout only to yield the 'newest' value of content_for.
EDIT:
I wrote a method to flush after an yield, suggesting that the same key would be used again:
def yield_and_flush!(content_key)
view_flow.content.delete(content_key)
end

content_for has flush option to reset previous content:
<% content_for :field, flush: true do %>
new content here
<% end %>

The solution was this to write an yield_and_flush! method. I saw the solution here
def yield_and_flush!(content_key)
view_flow.content.delete(content_key)
end

Related

Hash being saved to database but unable to render its values in Rails forms?

I added a new column to model Plan, named :per_unit_quantities_configuration which is a hash with min, max and step key/values.
t.jsonb "per_unit_quantities_configuration", default: {}
When I edit a Plan, the hash is being correctly saved to the DB (I can access each key/value from the console), but the forms are not displaying any of its values (the fields are empty).
I tried adding a store_accessor for the column in the Plan model, but it is not working:
store_accessor :per_unit_quantities_configuration, :min, :max, :step
Example of a simple_form html that does not display hash values:
<%= simple_form_for [:admin, #base_plan, #plan] do |f| %>
<% if f.object.base_plan.per_unit? %>
<div class="d-flex">
<%= f.simple_fields_for :per_unit_quantities_configuration do |fields| %>
<% if f.object.errors[:per_unit_quantities_configuration].any? %>
<%= f.error :per_unit_quantities_configuration, id: "per_unit_price_error", class: "invalid-feedback", error_prefix: "gato" %>
<% end %>
<%= fields.input :min %>
<%= fields.input :max %>
<%= fields.input :step %>
<% end %>
</div>
<% end %>
<%= f.button :submit, class: "pull-right" %>
<% end %>
What am I doing wrong?
since you setup store_accessor :per_unit_quantities_configuration then you can access directly 3 attributes min, max, step, so that you no need to wrap those attributes on simple_fields_for :per_unit_quantities_configuration and treat them as normal fields (that mean on controller you have to permit them as normal fields)
# view
<%= f.input :min %>
<%= f.input :max %>
<%= f.input :step %>
# controller
def plan_params
params.require(:plan).permit(:min,:max,:step)
end

Separating posts in a newsfeed using CSS/BootStrap

I'm currently trying to render a newsfeed, similar to that of FB on a Rails application I'm working on. Unfortunately, I'm not the greatest when it comes to CSS and I'm having some issues trying to display different posts. This issue occurs whether I'm using BootStrap or plain CSS. I do believe it's something to do with the loop that is created by <% #posts.each do |post| %> Currently, whenever a new post is made, it wraps inside the previous post; thus the more posts that are made, the thicker the border gets.
Image:
<% if #posts.any? %>
<% #posts.each do |post| %>
<div class="well">
<%= post.user.first_name %> <%= post.user.last_name %><br>
<% if !post.image.exists? %>
<h2> <%= post.text %> </h2>
<% else %>
<h2> <%= link_to post.text, post_path(post) %> </h2>
<%= link_to post_path(post) do %>
<p><%= image_tag post.image.url(:medium) %></p>
<% end %>
<% end %>
<% if #user %>
<% if current_user.voted_up_on?(post) %>
<%= link_to "Like", dislike_post_path(post), method: :put %>
<% else %>
<%= link_to "Like", like_post_path(post), method: :put %>
<% end %>
<% end %>
<%= "Likes: #{post.get_upvotes.size}" %>
<% if post.user == current_user %>
<%= link_to "Edit", edit_post_path(post) %>
<%= link_to "Delete", post_path(post), method: :delete %>
<% end %>
<div id='comments_div' class="comments-index">
<%= render post.comments %>
</div>
<% if current_user %>
<%= form_for [post, post.comments.new ], remote: true do |f| %>
<%= f.text_area :text, placeholder: 'Add a comment' %>
<%= f.submit 'Comment' %>
<% end %>
<% else%>
<p>You need to <%= link_to "sign in", new_user_session_path %> to comment</p>
<% end %>
<% end %>
<% else %>
No posts have been added!
<% end %>
</div>
Any help would be greatly appreciated! Thanks.
Edit: OK, please take a look at the new image -- hopefully that will make the issue slightly more obvious. Additionally, I've removed all the dead tags and replaced them with just one: BootStrap's 'well' class. So, there you have it. All the information you need is within the code above.
from your description it sounds as though an html element is not being properly closed. Run the page source through an html validator and that could show you the problem.
If you don't want to take a structured problem solving approach, try adding another </div> to the end of your posts-index container.
Your issue is very simple, just that its not clear due to poor indendation.
A simple way to explain what you did is:
<-- if (start) -->
<-- do (start) -->
<-- post (start) -->
(post is not ending here, hence it breaks the layout)
<-- do (end) -->
<-- if (end) -->
<-- post (end) -->
Mistake in the above should be simple to understand so if you move your last </div>(of the well class) just before the second last <% end %>(of the <% #posts.each do |post| %> loop) it should fix the issue. So the last few lines should be
<% else%>
<p>You need to <%= link_to "sign in", new_user_session_path %> to comment</p>
<% end %>
</div>
<% end %>
<% else %>
No posts have been added!
<% end %>
Sounds to me like it could be a misplaced
<% end %>
or a missing
</div>
that is causing this behavior.
Proper indentation will point to where to close off actions or divs

Conditionally setting html parameters in Rails

I have a pair of radio buttons that I want to pre-assign a checked value to only my new action. Right now I'm conditionally rendering two partials. One partial that has radio buttons with checked attributes and the other with not attributes at all:
<%= form_for([#restaurant, #dish_review], url: :restaurant_dish_reviews) do |f| %>
<% if action_name == "new" %>
<%= render "status_buttons_checked", f: f, dish: #dish %>
<% else %>
<%= render "status_buttons", f: f %>
<% end %>
<% end %>
_status_buttons_checked
<div class="field">
<%= f.radio_button :status, :upvoted, checked: current_user.voted_up_on?(dish) %>
<%= f.label :status, value: :upvoted %>
<%= f.radio_button :status, :downvoted, checked: current_user.voted_down_on?(dish) %>
<%= f.label :status, value: :downvoted %>
</div>
_statsus_buttons
<div class="field">
<%= f.radio_button :status, :upvoted, checked: current_user.voted_up_on?(dish) %>
<%= f.label :status, value: :upvoted %>
<%= f.radio_button :status, :downvoted, checked: current_user.voted_down_on?(dish) %>
<%= f.label :status, value: :downvoted %>
</div>
I was wondering if there was any way in Rails where I can insert the conditional in the radio_button parameter instead of creating two partials. I'd like to something similar to what's show below but run into a template error:
<%= f.radio_button :status, :downvoted, if action_name == "new" current_user.voted_down_on?(dish) %>
When using form_for, the form methods you use are automatically populated with the appropriate data for your attributes. Whilst I don't know if this works with the checked value, it means that if you have the following:
<%= form_for #user do |f| %>
<%= f.text_field :name %>
<% end %>
... :name will be populated from your #user object (if it's new, no data will be inserted).
--
This means that if you're using form_for, you should be able to populate the checked value with the data passed to the view:
<%= form_for [#restaurant, #dish_review] do |f| %>
<%= f.radio_button :status, :upvoted, checked: current_user.voted_up_on? #dish %>
<%= f.radio_button :status, :downvoted, checked: current_user.voted_down_on? #dish %>
<% end %>
I don't see what you're trying to achieve from your partials (they're both the same) -- but if you wanted to create "checked" on conditions, you'd be able to use the following:
<%= checked = action_name == "new"
<%= f.radio_button :status, :downvoted, checked: checked %>
This will set the value as "true" or "false" depending on whether the action is new or not.

Carrierwave images are not displaying, works everywhere else on the site

I'm using Carrierwave with Rails to upload and display images. I've been using it for articles and other parts of my site without a problem but for some reason it's not working for the model I just created. I can see the space for the image tag rendered without the image and then disappear quickly when the page loads. Nothing like this has occurred until I tried it with this model which isn't much different from my other models:
class Ad < ActiveRecord::Base
validates_numericality_of :zip_code
validates_presence_of :image, :advertiser
mount_uploader :image, ImageUploader
end
My create and index actions:
def create
#ad = Ad.new(params[:ad].permit(:image, :advertiser, :zip_code))
if #ad.save
redirect_to :back
else
flash[:error] = "Invalid input"
redirect_to :back
end
end
def index
#new_ad = Ad.new
#ads = Ad.all.reverse
end
My _new partial:
<div id="card">
<%= form_for #new_ad, html: {multipart: true, "data-ajax" => false} do |f| %>
<p>
<%= f.file_field :image %>
</p>
<p>
<%= f.text_field :advertiser, placeholder: "Advertiser" %>
</p>
<p>
<%= f.text_field :zip_code, placeholder: "Zip code" %>
</p>
<p>
<%= f.submit "Upload" %>
</p>
<% end %>
</div></br>
My index view:
<%= render "new" %>
<% for ad in #ads %>
<div id="card">
<div align="center">
<%= image_tag ad.image %>
<%= ad.advertiser %>
<%= ad.zip_code %>
</div>
</div></br>
<% end %>
As per my understanding problem is with your index view.
<%= image_tag ad.image %>
you are using ad.image this will return you imageuploader object not image path. So instead of using ad.image you can use ad.image.url(relative path) or ad.image.current_path(absolute path)
<%= image_tag ad.image.url %>

Assigning dom id based on model attributes in Rails nested form

I have a nested form.
Right now I want to arrange the layout with some CSS but I am facing trouble allocating dom ids to the form.
This is the subject controller.
I want to allocate lesson_type as seen in line 5 as the dom id.
1 def index
2 #subjects = Subject.all
3 #subject = Subject.new
4 lecture = #subject.lessons.build
5 lecture.lesson_type = "lecture"
lecture.lesson_groups.build
lecture.destroy
tutorial = #subject.lessons.build
tutorial.lesson_type = "tutorial"
tutorial.lesson_groups.build
tutorial.destroy
laboratory = #subject.lessons.build
laboratory.lesson_type = "laboratory"
laboratory.lesson_groups.build
laboratory.destroy
respond_to do |format|
format.html # index.html.erb
format.json { render json: #subjects }
format.js
end
end
The following is the form.
<%= nested_form_for(#subject, :remote=>true) do |f| %>
<% if #subject.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#subject.errors.count, "error") %> prohibited this subject from being saved:</h2>
<ul>
<% #subject.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :subject_code %><br />
<%= f.text_field :subject_code %>
</div>
<%= f.fields_for :lessons do |lesson| %>
<%= lesson.label :lesson_type %><br/>
<%= lesson.text_field :lesson_type, :readonly=>true%><br/>
<%= lesson.label :name %><br/>
<%= lesson.text_field :name %><br/>
<%= lesson.fields_for :lesson_groups do |lesson_group| %>
<%= lesson_group.label :group_index %><br/>
<%= lesson_group.text_field :group_index %>
<%= lesson_group.link_to_remove "Remove this task" %>
<% end %>
This is the div where I want to add an id to.
<%= f.fields_for :lessons do |lesson| %>
<%= lesson.label :lesson_type %><br/>
<%= lesson.text_field :lesson_type, :readonly=>true%><br/>
<%= lesson.label :name %><br/>
<%= lesson.text_field :name %><br/>
I have tried out the following but it did not worked.
<div id = "<%= :lesson_type%>">
Would appreciate it if someone could help me out thanks.
sorry..
#controller
def index
...
lecture.lesson_type = #lesson_dom_id = "lecture" # line 5
...
end
#view
<div id="<%= #lesson_dom_id %>">