So I have an interesting problem I'm working on. I am trying to create multiple objects of the same model in one view. I would like to display all the possible objects in my view, check boxes to select which ones to create, then submit and create all the corresponding objects.
Now the objects to select are gotten using an API request and returned in JSON format. The JSON is then displayed on the view for the user to select, then an array containing all the selected objects is sent back to the controller for creation.
Here is the relevant code that I've tried so far.
objects_controller.rb
def new
#possible_objects = <api call to get objs>
#objects = []
end
def create
params[:objects].each do |obj|
# create and save obj
end
end
objects/new.html.erb
<% form_for #objects do |f| %>
<% #possible_objects.each do |api_obj| %>
<%= check_box_tag(api_obj["name"])%>
<%= api_obj["name"] %>
<% end %>
<%= f.submit %>
<% end %>
This is definitely not the right approach, as the form will not accept an empty array as a parameter. I'm not sure where else to go with this, any pointers in the right direction would be great. Thanks.
Thanks to MrYoshiji for pointing me in the right direction, this is what ended up working
objects_controller.rb
def
#possible_objects = <api call to get objs>
end
def create
params[:objects].each do |object|
new_obj = Object_Model.new( <params> )
new_obj.save
if !new_obj.save
redirect_to <path>, alert: new_obj.errors.full_messages and return
end
end
redirect_to <path>, notice: 'Successfully created.'
end
objects/new.html.erb
<%= form_tag objects_path(method: :post) do %>
<% #possible_objects.each do |api_obj| %>
<%= check_box_tag 'objects[]', api_obj %>
<%= possible_object["name"] %>
<% end %>
<%= submit_tag 'Create'%>
<% end %>
Can you try the following?
# view
<% form_tag my_objects_path(method: :post) do |f| %>
<% #possible_objects.each do |api_obj| %>
<%= check_box_tag 'objects[names][]', api_obj["name"] %>
<%= api_obj["name"] %>
<% end %>
<%= f.submit %>
<% end %>
# controller
def create
params[:objects][:names].each do |obj_name|
YourModelForObject.create(name: obj_name)
end
end
See this comment on the documentation of check_box_tag: http://apidock.com/rails/ActionView/Helpers/FormTagHelper/check_box_tag#64-Pass-id-collections-with-check-box-tags
Related
I'm trying to create a helper method that will display {user.name} has no submitted posts." on the profile show view of user if they haven't yet submitted any posts and display the number posts they have . currently on my show view i have <%= render #user.posts %> which displays nothing when there are 0 posts submitted.
the partial for post is :
<div class="media">
<%= render partial: 'votes/voter', locals: { post: post } %>
<div class="media-body">
<h4 class="media-heading">
<%= link_to post.title, topic_post_path(post.topic, post) %>
<%= render partial: "labels/list", locals: { labels: post.labels } %>
</h4>
<small>
submitted <%= time_ago_in_words(post.created_at) %> ago by <%= post.user.name %> <br>
<%= post.comments.count %> Comments
</small>
</div>
</div>
ive tried :
def no_post_submitted?(user)
user.post.count(0)
"{user.name} has not submitted any posts yet."
end
on my user show view :
<%= if no_post_submitted?(#user) %>
<%= render #user.posts %>
which im more than sure is wrong but i have no idea how to implement this method .
Where you are using render #user.posts you can just add a simple conditional:
<% if #user.posts.empty? %>
<p><%= #user.name %> has no submitted posts</p>
<% else %>
<%= render #user.posts %>
<% end %>
There wouldn't be much point creating a helper for this unless you need to use it in multiple places.
Render collection returns nil if the collection is empty so you can use the || operator:
<%= render #user.posts || "{#user.name} has not submitted any posts yet." %>
Or if there is more code render another partial:
<%= render #user.posts || render 'no_posts' %>
In Ruby methods automatically return the last value so this method:
def no_post_submitted?(user)
user.post.count(0)
"{user.name} has not submitted any posts yet."
end
Will always return a string - if you use a string literal in a condition it will be evaluated as true with the warning warning: string literal in condition. Also that is not how you use count - passing 0 will cause it to query on column 0 or just error.
http://apidock.com/rails/ActiveRecord/Calculations/ClassMethods/count
So to fix the method you would do:
def no_post_submitted?(user)
user.posts.empty?
end
However that conditional is so simple that it does not really warrant a helper method. Instead you would just write:
<%= if user.post.any? %>
<%= render #user.posts %>
<% else %>
<%= "{user.name} has not submitted any posts yet." %>
<% end %>
There are a couple of problems with your solution. Remember, rails is more about convention over configuration.
Your method no_post_submitted? should actually return true/false since its a method ending with ?. Also it should be named no_posts_submitted? for clarity. It should look something like this:
def no_post_submitted?(user)
user.posts.count > 0
end
Then, there should be another helper method that will print your required message, Something like:
def no_posts_message(user)
"{user.name} has not submitted any posts yet."
end
And eventually you can all plug it in like this:
<% if no_posts_submitted?(user) %>
<%= no_posts_message(user) %>
<% else>
<%= render #user.posts %>
<% end %>
As per the docs:
In the event that the collection is empty, render will return nil, so it should be fairly simple to provide alternative content.
<h1>Products</h1>
<%= render(#products) || "There are no products available." %>
--
So...
<%= render(#user.posts) || "#{#user.name} has not submitted any posts yet." %>
After trying to accomplish the deceptively tricky task of building a form that enables the creation of multiple users at once, I wanted to pay it forward by sharing how I did it. Somewhat to my surprise, I was unable to find any SO answers that addressed this problem. Instead, the questions/answers kept addressing the scenario of creating a model as well as its associations on one form (using nested_attributes).
I must attribute the answer to a Ryan Bates' post from 2007 http://archive.railsforum.com/viewtopic.php?id=2696.
Your controller
def new
#owners = Array.new(3) { Owner.new } # set up any defaults here
end
def create
#owners = params[:owners].values.collect { |owner| Owner.new(owner) }
if #owners.all?(&:valid?)
#owners.each(&:save!)
redirect_to :action => 'index'
else
render :action => 'new'
end
end
Your View - new.erb.html
<% form_tag :action => 'create' do %>
<% #owners.each_with_index do |owner, index| %>
<% fields_for "owners[#{index}]", owner do |f| %>
First Name: <%= f.text_field :firstname %>
Last Name: <%= f.text_field :lastname %>
<% end %>
<% end %>
<%= submit_tag %>
<% end %>
I have the following logic in my view to choose which avatar picture to show depending on whether a persons profile is present
<% if #profile %>
<%= image_tag(#profile.avatar_url(:thumb)) %>
<% else %>
<%= image_tag(default_image_url) %>
<% end %>
Helper method
def default_image_url
hash = Digest::MD5.hexdigest(current_user.email)
"https://secure.gravatar.com/avatar/#{hash}?s=100&d=mm"
end
This works fine when someone has not created a profile, but when they do and still want to use their Gravatar this logic fails as my if condition then needs to be if
<% if #profile.avatar? %>
<%= image_tag(#profile.avatar_url(:thumb)) %>
<% else %>
<%= image_tag(default_image_url) %>
<% end %>
At the moment when a profile is created with no image uploaded by the user, there is no image displayed at all.
How can I cover all scenarios?
Edit
I'm in the process of trying
<% unless #profile || #profile.avatar %>
A bit of refactoring starting from #ArieShaw's answer:
Helper
def profile_image_url
#profile.try(:avatar?) ? #profile.avatar_url(:thumb) : default_image_url
end
View
<%= image_tag profile_image_url %>
You may use Object#try:
<% if #profile.try(:avatar?) %>
<%= image_tag(#profile.avatar_url(:thumb)) %>
<% else %>
<%= image_tag(default_image_url) %>
<% end %>
I am really embarrassed with this problem :
I want to post comments from one view and if errors occurs in form, i want the form to be repopulated. I use the render method but my form isn't repopulated.
I specify that the form is displayed from a view and use another controller action, by other words means :
Form called from : views/cars/show.html.erb code below :
<h1>Fiche détaillée</h1>
<%= #car.marque %><br>
<%= #car.modele %><br>
<%= #car.nbkm %><br>
<%= #car.couleur %><br>
<%= #car.disponibilite %><br>
<hr>
<% x=0 %>
<h1><%= pluralize(#car.comments.count, 'commentaire') %></h1>
<% #car.comments.each do |k| %>
<%= x+=1 %>
Email : <%= k.email %><br>
Sujet : <%= k.sujet %><br>
Commentaire : <%= k.commentaire %><br>
<%= link_to 'Supprimer', [k.car, k], method: :delete %><br><br>
<% end %>
<hr>
<h1>Ajouter votre commentaire</h1>
<div style='width:300px;'>
<% flash.each do |key, msg| %>
<% if msg.count >0 %>
<p class="bg-danger" style='padding:10px;'><%= pluralize(msg.count,'error') %>
<ul><% msg.full_messages.each do |m|%>
<li><%= m %></li>
<% end %>
</p>
<% end %>
<% end %>
</ul>
<%= form_for([#car,#car.comments.build]) do |co| %>
<%= co.label :'Email' %><br>
<%= co.text_field :email , class: 'form-control' %><br>
<br>
<%= co.label :'Sujet' %><br>
<%= co.text_field :sujet , class: 'form-control'%><br>
<br>
<%= co.label :'Commentaire' %><br>
<%= co.text_area :commentaire , class: 'form-control' %><br>
<br>
<%= co.submit :'Envoyer votre commentaire', class: 'btn btn-info'%>
<% end %>
</div>
below my controllers :
Controller 1 : controllers/cars_controller.rb
def create
#render text: params[:car].inspect
#car = Car.new(params[:car].permit(:marque,:modele,:nbkm,:couleur,:disponibilite))
if !#car.save
render 'new'
else
redirect_to #car
end
end
def show
#car = Car.find(params[:id])
end
def index
#cars=Car.all
end
Controller 2 : controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
#comment=Comment.new
end
def create
#car = Car.find(params[:car_id])
#comment = #car.comments.create(params[:comment].permit(:email,:sujet,:commentaire))
if !#comment.save
flash[:error] = #comment.errors
flash.keep[:error]
render 'cars/show'
else
redirect_to car_path(#car)
end
end
def destroy
#car = Car.find(params[:car_id])
#comment = #car.comments.find(params[:id])
#comment.destroy
redirect_to car_path(#car)
end
end
I really don't understand why it does not work !!
Thank you so much for any assistance ;)
Edited:
I did some similar tests a bit for your case, the problem should due to the flash method.
Replace below lines:
flash[:error] = #comment.errors
flash.keep[:error]
render 'cars/show'
To:
flash.now[:error] = #comment.errors.full_messages
#car.reload
render 'cars/show'
Because flash[:error] will only be available in next action, means only works in redirect_to, so you have to use flash.now[:error] for rendering same view template. And most importantly, though the save failed of the #comment, the comment list in #car will still receive an instantiated invalid object return by the comment's create, build, or new method. It is because these three methods will always return instantiated object to the #car.comments collection, though it is failed to save it. So we must reload the #car object by #car.reload to refresh the memory and get correct Comment collection from the database.
Previous response:
In crate action, the create method #car.comments.create(..) will directly create and return an instantiated object without given attributes then try to save it if it passed the validation. If you have not set validations for Comment model, then it will directly save it. Try #car.comments.new(..) or #car.comments.build(..) for collection associations, it will not force to save an instantiated object after the validation passed. Also, check your Comment model for setting the validations.
I'm developing a rails application.
I want the user to be able to make a selection between an array of models
In one controller, I create an array of models.
def myController
#data = []
#data += [MyData.find(2)]
#data += [MyData.find(5)]
#data += [MyData.find(7)]
end
In the view, I can't use the form_for because can't be used in an array, so I have:
<%= form_tag 'myOp' do |f|%>
<%= fields_for :test, #data do |builder|%>
<%= render 'sub_form', :f => builder %>
<% end %>
<% end %>
Now in the sub_form, I want to recieve each of the items of the array, but instead, I'm getting the full array.
How can I get each of the items of the array in the subform?
Is there a better way to do this?
Thanks
So first in your controller
def my_action
#datas = MyData.find(2, 5, 7)
end
Then in your view
You need to iterate through the #datas array and yield the fields for each object. That is because fields_for yields fields for one object only, not arrays of objects.
<%= form_tag 'myOp' do |f|%>
<% #datas.each_with_index do |data, i| %>
<%= fields_for "test_#{i}", data do |builder|%>
<%= render 'sub_form', :f => builder %>
<% end %>
<% end %>
<% end %>
I hope this will correct the issue:
<%= form_tag 'myOp' do |f|%>
<%= fields_for :test, #data.each do |builder|%>
<%= render 'sub_form', :f => builder %>
<% end %>
<% end %>
Normally an array object can be seperated using .each method. May that would work here also. Try it.