passing an object to render( :template => "template.json.rabl") in RoR 3 - ruby-on-rails

I need to embed json into html and in the #322 RailsCasts says the way to do it using RABL...
app/views/places/show.html.erb
<div id="place" data-place="<%= render(template: "places/show.json.rabl") %>" >
here is my rabl template
app/views/places/show.json.rabl
object #place
attributes :id, :name, :desc, :address
node(:type) { |place| place.type_place.name }
child :photos do
attributes :id, :desc
node(:url) { |photo| photo.image.url(:square) }
end
and It is working fine but I want to render the rabl-template in other view like:
app/views/places/finder.html.erb
<%= #places.each do |place| %>
<div id="place-<%= place.id %>" data-place="<%= render(template: "places/show.json.rabl") %>" >
<% end %>
it shows me the next message error
undefined method `type_place' for nil:NilClass
Extracted source (around line #1):
1: object #place
2: attributes :id, :name, :desc, :address
3:
4: node(:type) { |place| place.type_place.name }
from the message I think the error is what I'm not passing the object place to the template... I was trying a lot and I didn't get this.
Thanks in advance, and sorry for my english

You need to explicitly call Rabl::Renderer
<%= #places.each do |place| %>
<div id="place-<%= place.id %>" data-place="<%= Rabl::Renderer.json(place, 'places/show', view_path: 'app/views') %>" >
<% end %>

Related

html tags rendered for nested association doesn't get wrapped in field_with_errors div

Rails 5.1.4, ruby:2.3.7
I have a nested form for a parent model and children model with a validation on the uniqueness of the label in the child model in scope of the parent one. The issue is when I submit the form with identical names in both forms non of them get wrapped in the field_with_errors class. I need the html tag to be wrapped in that class so I can subsequently render errors on the form using bootstrap 4 invalid-feedback and invalid css classes.
How can I render the form with the field_with_errors div wrapping the text_field for the dashboard_label?
The models are as follows:
class Project < ApplicationRecord
has_many :study_media_files
end
class StudyMediaFile < ApplicationRecord
belongs_to :project
validates :dashboard_label, uniqueness: { scope: :project }
end
The controller code is as follows:
class DsasController < ApplicationController
def show
#project = Mrcore::Project.find params[:id]
end
def update
#project = Mrcore::Project.find params[:id]
if(#project.update project_params)
redirect_to #project, notice: 'updated successfully'
else
render 'show'
end
end
private
def project_params
params.require(:project).permit(study_media_files_attributes: %i[id dashboard_label show])
end
end
The view:
<%= f.fields_for :study_media_files, #project.study_media_files.non_zero_duration do |media_form| %>
<div class="row ">
<div class="col-4 small">
<%= media_form.object.affdex_movie_id %>
</div>
<div class="col-5">
<%= media_form.text_field :dashboard_label, class: 'input-sm col-lg form-control', required: true %>
</div>
<div class="col-2 float-right">
<%= media_form.check_box :show, { class: 'form-check-input ml-2' } %>
</div>
</div>
<% end %>
It seems that for some reason the object's dashboard_label gets reset to its original value.
The issue is that the view with the fields for function loads the data from the database, I guess and overrides the errors that are transient on the project object, using a condition in the view code as below solved the issue.
<%= f.fields_for :study_media_files do |media_form| %>
<% if media_form.object.duration > 0 %>
<div class="row ">
<div class="col-4 small">
<%= media_form.object.affdex_movie_id %>
</div>
<div class="col-5">
<%= media_form.text_field :dashboard_label, class: 'input-sm col-lg form-control' %>
</div>
<div class="col-2 float-right">
<%= media_form.check_box :show, { class: 'form-check-input ml-2' } %>
</div>
</div>
<% end %>
<% end %>
Essentially moving the collection on the fields_for block to a condition inside the block.

Rails Strong Parameters - Array of Hashes - Multiple Records in Single Form

I'm trying to submit multiple records in a single form in a rail 5.2 app. I'm not using accepts nested attributes of another model, as it always seemed to bring in the entries that were previously created. The entries can't be edited once created.
<%= form_tag entry_details_path do %>
<% #entries.each do |entry| %>
<%= fields_for 'entries[]', entry do |e| %>
<div class="field">
<%= e.label :user_account_id %><br>
<%= e.text_field :user_account_id %>
</div>
<div class="field">
<%= e.label :amount %><br>
<%= e.text_field :amount %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>
The params looks like:
"entries"=>[<ActionController::Parameters {"user_account_id"=>"3", "amount"=>"55"} permitted: false>, <ActionController::Parameters {"user_account_id"=>"4", "amount"=>"65"} permitted: false>]
I want to pash the entire array of hashes to a create such as:
def create_multiple_entries
#entries = Entries.create(multiple_entries_params)
#entries.reject! { |e| e.errors.empty? }
if #entries.empty?
redirect_to entries_path
else
render 'new'
end
end
The method above works if manually enter an array. My challenge is the strong parameters(I think). Right now I have:
def leave_accrual_details_params
params.require(:entries).permit([:user_account_id, :amount])
end
Which gives me the following error:
!! #<NoMethodError: undefined method `permit' for #<Array:0x00007fee4014ec78>>
Try the following for Rails 5:
def leave_accrual_details_params
params.require(:entries).map do |p|
p.permit(:user_account_id, :amount)
end
end

Rendering a partial in rails. Specifying the partial for a resource gives an error, but not specifying a partial works fine. What gives?

I've got this working now quite accidentally, but I don't understand what causes it to break when I explicitly specify what partials are to be used for rendering the resource/s. Can anyone explain it?
The index template for my Posts controller contained the following line, which was giving me an error:
<%= render partial: 'posts', collection: #posts %>
The error (in my browser) said:
NoMethodError in Posts#index
Showing /Users/applebum/Sites/rails_projects/eventful2/app/views/posts/_posts.html.erb where line #1 raised:
undefined method `any?' for #<Post:0x000001064b21f0>
Extracted source (around line #1):
1: <% if posts.any? %>
2: <div id="posts">
3: <% posts.each do |post| %>
4: <%= render partial: "posts/post", locals: { post: post } %>
Changing the problem line to
<%= render #posts %>
made the error disappear and the posts appear (displayed nicely in markup from the appropriate partials) as I had wanted and expected them to.
Here's my _posts.html.erb partial:
<% if posts.any? %>
<div id="posts">
<% posts.each do |post| %>
<%= render partial: "posts/post", locals: { post: post } %>
<% # render :partial => "comments/comments", :collection => post.comments %>
<% end %>
</div>
<% end %>
And the _post.html.erb partial it's referring to, if that matters:
<div class="post" id="post_<%= "#{post.id}" %>">
<div class="post_inner">
<%= link_to avatar_for(post.user, size: "small"), post.user.profile %>
<div class="post_body">
<div class="user-tools">
<% if can? :destroy, post %>
<%= link_to '<i class="fi-x"></i>'.html_safe, post, :method => :delete, remote: true, :class => "delete", :confirm => "Are you sure you want to delete this post?", :title => post.content %>
<% end %>
</div>
<h5 class="username">
<%= link_to post.user.name, post.user.profile %>
<span class="timestamp">• <%= time_ago_in_words(post.created_at) %> ago</span>
</h5>
<div class="content">
<%= post.content %>
</div>
<ul class="foot">
<li>Like<li>
<li>Share</li>
</ul>
</div>
</div>
</div>
And the relevant bits from the controller:
class PostsController < ApplicationController
respond_to :html, :js # Allow for AJAX requests as well as HTML ones.
before_filter :load_postable
load_and_authorize_resource
def index
#post = Post.new
#posts = #postable.posts
end
private #################
def load_postable
klass = [User, Event].detect { |c| params["#{c.name.underscore}_id"] } # Look for which one of these there's a ***_id parameter name for
#postable = klass.find(params["#{klass.name.underscore}_id"]) # Call find on that, passing in that parameter. eg Event.find(1)
end
Can anyone explain to me what's going on here? I couldn't find anything in the Layouts and Rendering guide at rubyonrails.org.
Thanks!
Your error comes from assuming :collection and #posts mean the same thing when rendering. From Rails Docs (point 3.4.5):
Partials are very useful in rendering collections. When you pass a collection to a partial via the :collection option, the partial will be inserted once for each member in the collection
So, if you use that, for each post, you will be doing post.any? which fails as any? isn't defined for a single post.
From the same docs, you should check if render returns Nil to see if the collection is empty:
<h1>Posts</h1>
<%= render(#posts) || "There are no posts." %>
PD: Use the partial to render only one post, not all of them.
GL & HF.

Rails: fields_for with index?

Is there a method (or way to pull off similar functionality) to do a fields_for_with_index?
Example:
<% f.fields_for_with_index :questions do |builder, index| %>
<%= render 'some_form', :f => builder, :i => index %>
<% end %>
That partial being rendered needs to know what the current index is in the fields_for loop.
The answer is quite simple as the solution is provided within Rails. You can use f.options params. So, inside your rendered _some_form.html.erb,
Index can be accessed by:
<%= f.options[:child_index] %>
You don't need to do anything else.
Update: It seems that my answer wasn't clear enough...
Original HTML File:
<!-- Main ERB File -->
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder %>
<% end %>
Rendered Sub-Form:
<!-- _some_form.html.erb -->
<%= f.options[:child_index] %>
As of Rails 4.0.2, an index is now included in the FormBuilder object:
https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for
For example:
<%= form_for #person do |person_form| %>
...
<%= person_form.fields_for :projects do |project_fields| %>
Project #<%= project_fields.index %>
...
<% end %>
...
<% end %>
The answer below was posted many years ago, for a modern approach see:
https://stackoverflow.com/a/22640703/105403
This would actually be a better approach, following Rails documentation more closely:
<% #questions.each.with_index do |question,index| %>
<% f.fields_for :questions, question do |fq| %>
# here you have both the 'question' object and the current 'index'
<% end %>
<% end %>
From:
http://railsapi.com/doc/rails-v3.0.4/classes/ActionView/Helpers/FormHelper.html#M006456
It’s also possible to specify the
instance to be used:
<%= form_for #person do |person_form| %>
...
<% #person.projects.each do |project| %>
<% if project.active? %>
<%= person_form.fields_for :projects, project do |project_fields| %>
Name: <%= project_fields.text_field :name %>
<% end %>
<% end %>
<% end %>
<% end %>
For Rails 4+
<%= form_for #person do |person_form| %>
<%= person_form.fields_for :projects do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
Monkey Patch For Rails 3 Support
To get f.index to work in Rails 3 you need to add an monkey patch to your projects initializers to add this functionality to fields_for
# config/initializers/fields_for_index_patch.rb
module ActionView
module Helpers
class FormBuilder
def index
#options[:index] || #options[:child_index]
end
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
fields_options[:namespace] = options[:namespace]
case record_name
when String, Symbol
if nested_attributes_association?(record_name)
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
end
else
record_object = record_name.is_a?(Array) ? record_name.last : record_name
record_name = ActiveModel::Naming.param_key(record_object)
end
index = if options.has_key?(:index)
options[:index]
elsif defined?(#auto_index)
self.object_name = #object_name.to_s.sub(/\[\]$/,"")
#auto_index
end
record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
fields_options[:child_index] = index
#template.fields_for(record_name, record_object, fields_options, &block)
end
def fields_for_with_nested_attributes(association_name, association, options, block)
name = "#{object_name}[#{association_name}_attributes]"
association = convert_to_model(association)
if association.respond_to?(:persisted?)
association = [association] if #object.send(association_name).is_a?(Array)
elsif !association.respond_to?(:to_ary)
association = #object.send(association_name)
end
if association.respond_to?(:to_ary)
explicit_child_index = options[:child_index]
output = ActiveSupport::SafeBuffer.new
association.each do |child|
options[:child_index] = nested_child_index(name) unless explicit_child_index
output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
end
output
elsif association
fields_for_nested_model(name, association, options, block)
end
end
end
end
end
Checkout Rendering a collection of partials. If your requirement is that a template needs to iterate over an array and render a sub template for each of the elements.
<%= f.fields_for #parent.children do |children_form| %>
<%= render :partial => 'children', :collection => #parent.children,
:locals => { :f => children_form } %>
<% end %>
This will render “_children.erb“ and pass the local variable 'children' to the template for display. An iteration counter will automatically be made available to the template with a name of the form partial_name_counter. In the case of the example above, the template would be fed children_counter.
Hope this helps.
I can't see a decent way to do this through the ways provided by Rails, at least not in -v3.2.14
#Sheharyar Naseer makes reference to the options hash which can be used to solve the problem but not as far as I can see in the way he seems to suggest.
I did this =>
<%= f.fields_for :blog_posts, {:index => 0} do |g| %>
<%= g.label :gallery_sets_id, "Position #{g.options[:index]}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<%# g.options[:index] += 1 %>
<% end %>
or
<%= f.fields_for :blog_posts do |g| %>
<%= g.label :gallery_sets_id, "Position #{g.object_name.match(/(\d+)]/)[1]}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<% end %>
In my case g.object_name returns a string like this "gallery_set[blog_posts_attributes][2]" for the third field rendered so I just match the index in that string and use it.
Actually a cooler (and maybe cleaner?) way to do it is to pass a lambda and call it to increment.
# /controller.rb
index = 0
#incrementer = -> { index += 1}
And the in the view
<%= f.fields_for :blog_posts do |g| %>
<%= g.label :gallery_sets_id, "Position #{#incrementer.call}" %>
<%= g.select :gallery_sets_id, #posts.collect { |p| [p.title, p.id] } %>
<% end %>
Added to fields_for child_index: 0
<%= form_for #person do |person_form| %>
<%= person_form.fields_for :projects, child_index: 0 do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
I know that this is a bit late but I recently had to do this you can get the index of the fields_for like this
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder, :i => builder.options[:child_index] %>
<% end %>
I hope that this helps :)
If you want to have control over the indexes check out the index option
<%= f.fields_for :other_things_attributes, #thing.other_things.build do |ff| %>
<%= ff.select :days, ['Mon', 'Tues', 'Wed'], index: 2 %>
<%= ff.hidden_field :special_attribute, 24, index: "boi" %>
<%= end =>
This will produce
<select name="thing[other_things_attributes][2][days]" id="thing_other_things_attributes_7_days">
<option value="Mon">Mon</option>
<option value="Tues">Tues</option>
<option value="Wed">Wed</option>
</select>
<input type="hidden" value="24" name="thing[other_things_attributes][boi][special_attribute]" id="thing_other_things_attributes_boi_special_attribute">
If the form is submitted, params will include something like
{
"thing" => {
"other_things_attributes" => {
"2" => {
"days" => "Mon"
},
"boi" => {
"special_attribute" => "24"
}
}
}
I had to use the index option to get my multi-dropdowns to work. Good luck.

Why I can't send a post method in RoR?

I have this method to post the value to the "/store/add_to_cart"
<form action = "/store/add_to_cart" method="post">
<% for product in #products -%>
<div class = "entry">
<%= product.title %>
<%= product.price %>
<p>
</div>
<% end %>
<%= select( "payment", "id", { "Visa" => "1", "Mastercard" => "2"}) %>
<%= submit_tag 'Make Order' %>
</form>
In the /store/add_to_cart.html.erb, I created :
<%= params.length %>
<% for i in params%>
<%=i%>
<br/>
<% end %>
But I get this error:
ActionController::InvalidAuthenticityToken in StoreController#add_to_cart
What's happen? but after I change it to the get method, I can get all the params, wt's happen?
You are not using rails form_for helper to generate the <form> HTML markup, what this method does in addition is to add a hidden input field that is used to prevent CSRF attacks.
You have three options:
Use the form_for, form_tag... helper
Include the hidden input yourself
Disable the CSRF support

Resources