Iterate through an array to create rails objects - ruby-on-rails

I am having trouble finding any information on how to iterate through an array and create objects.
My form creates a selectable list of users that when checked, pass the user_ids as an array object.
invitations\new.html.rb
<%= bootstrap_form_for Invitation.new do |f| %>
<br>
<ul>
<%= f.hidden_field :attended_event_id, :value => #event_selected.id %>
<li>
<%= check_box_tag 'attendee_id[]', user.id %>
<%= h user.name %>
</li>
<% end %>
</ul>
<br>
<%= f.submit "Invite Selected Users" %>
<% end %>
I would like to then create new Invitations objects by combining the attended_event_id with all of the objects in the attendee_id array.
After a bit of trouble I got the basics of my controller working but only by passing in the user_id as a text entry. Below is my Invitations controller. Not really sure where to start on this one as I haven't been able to find a good example.
invitations_controller.rb
def create
#invitation = Invitation.new(invite_params)
if #invitation.save!
flash.now[:success] = "Invited!"
redirect_to root_path
else
flash.now[:error] = "Failure!"
redirect_to root_path
end
end
private
def invite_params
params.require(:invitation).permit(:attended_event_id, :attendee_id)
end
end

Do you mean something like this?
<%= bootstrap_form_for Invitation.new do |f| %>
<br>
<ul>
<%= f.hidden_field :attended_event_id, :value => #event_selected.id %>
<% users.each do |user| %>
<li>
<%= check_box_tag 'invitation[attendee_id][]', user.id %>
<%= h user.name %>
</li>
<% end %>
</ul>
<br>
<%= f.submit "Invite Selected Users" %>
<% end %>
def create
#invitations = invite_params[:attendee_id].map do |attendee_id|
Invitation.new(
attended_event_id: invite_params[:attended_event_id],
attendee_id: attendee_id
)
end
if #invitations.any?(&:invalid?)
flash.now[:error] = "Failure!"
redirect_to root_path
else
#invitations.each(&:save!)
flash.now[:success] = "Invited!"
redirect_to root_path
end
end
private
def invite_params
params.require(:invitation).permit(:attended_event_id, {:attendee_id => []})
end

There is a good basic example on RailsGuides
http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object

Do you want to achieve something like this:
def create
params[:attendee_id].each do |user_id|
Invitation.create(:attended_event_id => params[:attended_event_id], :attendee_id => user_id)
end
.
.
.
end

Related

Rails 4 : Table Boolean Column Update Using "link_to "with a specific value "TRUE" always

In my customer controller the update method code is like bellow:
def update
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
In my view in customers index page I am planning to add a "link_to" link, if it is clicked, then that particular customers field "doc_delete" should be updated with value "TRUE".
<td><%= link_to "[Update", *************what is here ?******** method: :put %></td>
You can pass hidden params through button_to:
<%= button_to "Update", user, method: :put, params: { doc_delete: true } %>
This will create a micro-form, much like what Marwen alluded to. Whilst quite inefficient, it will be the best way to send data to your update action.
--
Another, more efficient, way would be to define a custom route/action:
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/users/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
redirect_to customers_path if #customer.update doc_delete: true
end
end
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<%= link_to "Update", customer_doc_delete_path(customer) %>
<% end %>
You will need a form to do that for you
<% unless customer.doc_delete? %>
<%= form_for customer do |f| %>
<%= f.hidden_field_tag :doc_delete, true %>
<%= f.submit %>
<% end %>
<% end %>
Where to insert this form?
Well if you are rendering you costumers using:
<%=render #costumers %>
then you will add the form in the /customers/_customer.html.erb
If you are looping them manually:
<% #customers.each do |customer| %>
<%=customer.full_name %>
## Here you can add the form
<% end %>
An another way, you can use Ajax.
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<% if !customer.doc_delete == true %>
<%= link_to "Update", customer_doc_delete_path(customer), remote: true %>
<% else %>
<%= Updated %>
<% end %>
<% end %>
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/customers/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
if #customer.update doc_delete: true
respond_to do | format |
format.js {render :nothing => true}
end
end
end
end
In my index.html
<td>
<%= hidden_field_tag 'delete_present', :value => "present" %>
<%=link_to "[update]", customer_path(customer, :doc_delete => true), :method => :put, :confirm => "Are you sure?" %>
</td>
In my customer controller
def update
if params[:doc_delete].present?
#customer= Customer.find(params[:id])
#customer.doc_delete=true
#customer.save
redirect_to customers_path
else
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
end

How to redirect two buttons on a different path with the same action?

I try to create discount coupons subtracting total basket of my customers.
I'm not sure it's the best way to do it but I created a solution that works almost.
The only problem is that when I create coupon, I can't render the same page because my order's update method redirect to the checkout page. I want to different redirections, one for checkout when the customer click on the checkout button on my cart page, the other when the customer create a coupon on the same cart page. The two buttons uses the same update action.
Any idea how to solve it ?
Here's my orders_controller:
def update
#order = current_order
update_coupon
if #order.update(order_params)
redirect_to checkout_path
else
render 'new'
end
end
private
def update_coupon
if #order.update(:coupon => params[:order])
redirect_to cart_path
end
end
Here's my carts/show.html.erb:
<p>Total TTC: <%= number_to_currency #order.subtotal %></p>
<% if #order.add_reduc.nil? %>
<% else %>
<p style="color:green;">-<%= number_to_currency #order.add_reduc, id: "new_reduc" %></p>
#set coupon value to nil
<%= form_for #order do |f| %>
<%= f.hidden_field :coupon, :value => nil %>
<%= f.submit "x" %>
<% end %>
<p>Price after reduction: <%= number_to_currency #order.subtotal_with_reduc %></p>
<p style="color:green;"><%= #order.coupon_description %></p>
<% end %>
<%= form_for #order do |f| %>
<%= f.text_field :coupon, placeholder:'place your coupon' %>
<%= f.submit "Go coupon" %>
<% end %>
and my order.rb :
COUPONS = {
'MAREDUC' => '25% off',
'CHOCOLOVER' => '10€ free',
'PAPLAFUN' => '10% off'
}
def subtotal_all_inclusive
if self.add_reduc.nil?
subtotal + shipping
else
subtotal_with_reduc + shipping
end
end
def coupon_description
COUPONS[coupon]
end
def add_reduc
if self.coupon == "MAREDUC"
subtotal * 25 / 100
elsif self.coupon == "CHOCOLOVER" && self.subtotal >= 50
10
elsif self.coupon == "PAPLAFUN"
subtotal * 10 /100
else
nil
end
end
You may add a hidden field in the coupon form:
<%= form_for #order do |f| %>
<%= hidden_field_tag :from_coupon, 1 %>
<%= f.text_field :coupon, placeholder:'place your coupon' %>
<%= f.submit "Go coupon" %>
And then:
def update
#order = current_order
if params[:from_coupon]
update_coupon
else
if #order.update(order_params)
redirect_to checkout_path
else
render 'new'
end
end
end
Or you may create an additional action in routes.rb:
resources :orders do
member do
put 'update_coupon'
end
end
And then:
<%= form_for #order, :url => update_coupon_order_path(#order), :method => :put do |f| %>
And then remove private in controller.
Pass additional param to url and check if it present make different actions. Example
<%= form_for #order, url: order_path(#order, additional: "your_param") do |f| %>

Multiple update forms for one model

I want to have multiple forms on one page. Let's make an example to understand what I want:
I have a page for my admins, let's say it's the admins#show page. My admin has to change his name on one form on this page and on another form his age. I know I could create one form but I want to have multiple forms (because this is just an example). So my admins#show page looks something like this:
<%= form_for #admin do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for #admin do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
But in my controller, I don't know really how this works and here is my problem. I think I have something like this, but how could I divide the form inputs in the update method?:
def edit
#admin = Admin.find(params[:id])
end
def update
#admin= Admin.find(params[:id])
if #admin.update_attributes(:name=> admin_params1[:name])
redirect_to #admin
else
render 'edit'
end
if #admin.update_attributes(:age=> admin_params2[:age])
redirect_to #admin
else
render 'edit'
end
end
private
def admin_params1
params.require(:admin).permit(:name)
end
def admin_params2
params.require(:admin).permit(:age)
end
Its a bit Unorthodox what you are doing, but as you insisted and only its an example, I guess you can handle the update method by doing like this
def update
#admin= Admin.find(params[:id])
if params[:commit] == "Submit name change"
if #admin.update_attributes(admin_params1)
redirect_to #admin
else
render 'edit'
end
elsif params[:commit] == "Submit age change"
if #admin.update_attributes(admin_params2)
redirect_to #admin
else
render 'edit'
end
end
end
Note: Not Tested!
Well, I think you could create other non-REST methods in the controller and then add named routes in your config/routes then add your two different forms similar to this;
<%= form_for :admin_name, url: admin_name_path, method: :post do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for :admin_age, url: admin_age_path, method: :post do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
Then something like this;
def update_age
#admin = Admin.find(params[:admin_age][:id])
if params[:admin_age]
#admin.update_attributes(:age=> params[:admin_age][:age])
redirect_to #admin
else
render 'edit'
end
end
def update_name
#admin = Admin.find(params[:admin_name][:id])
if params[:admin_name]
#admin.update_attributes(:name=> params[:admin_name][:name])
redirect_to #admin
else
render 'edit'
end
end
** not tested for bugs

Incorrect param submitting

I have a form for casting your vote for your favourite image.
<%= form_for(#imagevote) do |f| %>
<% #miniature.collections(:photo).each do |collection| %>
<% if collection.photo.exists? %>
<td><div class="photo1">
<%= link_to image_tag(collection.photo.url(:thumb), :retina => true), collection.photo.url(:original), :retina => true, :class => "image-popup-no-margins" %>
<%= f.radio_button(:collection_id, collection.id) %>
<%= f.hidden_field :voter_id, :value => current_user.id %>
<%= f.hidden_field :voted_id, :value => collection.user_id %>
<%= f.hidden_field :miniature_id, :value => #miniature.id %>
<p>Painted by <%= link_to collection.user.name, collection.user %></p>
</div></td>
<% end %>
<% end %>
<%= f.submit "Vote" %>
<% end %>
Everything submits correctly except for the hidden_field :voted_id which for some reason duplicates the current_user.id.
UPDATE
I've tried logging in as another user and it seems that :voted_id is not duplicating current_user.id but rather that it is always "7" which was the :user_id I was using to test it before. Now logged in as user number 4 it is still entering the :voted_id as 7. I'm lost.
The link to the imagevotes view is as follows:
<%= link_to "See more and change your vote.", edit_imagevote_path(:miniature_id => #miniature, :voter_id => current_user.id) %>
Here is my image votes controller
class ImagevotesController < ApplicationController
respond_to :html, :js
def new
#imagevote = Imagevote.new
#miniature = Miniature.find(params[:miniature_id])
end
def edit
#imagevote = Imagevote.find_by_miniature_id_and_voter_id(params[:miniature_id],params[:voter_id])
#miniature = Miniature.find(params[:miniature_id])
end
def create
#imagevote = Imagevote.new(imagevote_params)
if #imagevote.save
flash[:success] = "Vote registered"
redirect_to :back
else
flash[:success] = "Vote not registered"
redirect_to :back
end
end
def update
#imagevote = Imagevote.find(params[:id])
if #imagevote.update_attributes(imagevote_params)
flash[:success] = "Vote changed."
redirect_to :back
else
redirect_to :back
end
end
private
def imagevote_params
params.require(:imagevote).permit(:collection_id, :voter_id, :voted_id, :miniature_id)
end
end
You only have one #imagevote object, but you are outputting the hidden fields inside your collection loop so you will have multiple fields in the form referencing the same attribute on the model: if you check the html that is generated, you should see multiple hidden fields with the same name attribute.
The way that browsers handle multiple inputs with the same name means that the param that comes through for :voted_id will always be the :user_id from the last collection.
It's difficult to say because you didn't provide your model and your loop code stripped.
I would guess that you loop over collection that belongs to the current_user. And in this case you will have current_user.id always be the same as collection.user_id. May be you wanted to see collection.photo_id?

No route matches - missing required keys: [id] ?

I'm doing a Rails tutorial, and trying to figure out why this is happening.
I'm making a to-do list, and everytime I try and insert a record into my Todo model, I get the following:
Here is the new.html.erb view that this is from:
<h1>Add new item to your todo listM</h1>
<%= form_for #todo, :url=>todo_path(#todo) do |f| %>
<%= f.label :name %> <%= f.text_field :name%>
<%= f.hidden_field :done, :value => false %>
<%= f.submit "Add to todo list" %>
<% end %>
Here is index.html.erb from where the user is linked to new.html.erb
<h1>TASKS</h1>
<h3> TO DO </h3>
<ul>
<% #todos.each do |t| %>
<li>
<strong><%= t.name %></strong>
<small><%= link_to "Mark as Done", todo_path(t), :method => :put %></small>
</li>
<% end %>
</ul>
<h3> DONE </h3>
<ul>
<% #todones.each do |t| %>
<li>
<strong><%= t.name %></strong>
<small><%= link_to "Remove", t, :confirm => "You sure?", :method => :delete %></small>
</li>
<% end %>
</ul>
<%= link_to "Add new task", new_todo_path %>
Here is the TodoController I have managing these actions:
class TodoController < ApplicationController
def index
#todos = Todo.where(done:false)
#todones = Todo.where(done:true)
end
def new
#todo = Todo.new
end
def todo_params
params.require(:todo).permit(:name, :done)
end
def create
#todo = Todo.new(todo_params)
if #todo.save
redirect_to todo_index_path, :notice => "Your todo item was created!"
else
render "new"
end
end
def update
#todo = Todo.find(params[:id])
if #todo.update_attribute(:done, true)
redirect_to todo_index_path, :notice => "Your todo item was marked done!"
else
redirect_to todo_index_path, :notice => "Couldn't update your task"
end
end
def destroy
#todo = Todo.find(params[:id])
#todo.destroy
redirect_to todo_index_path, :notice => "Your todo item was deleted"
end
end
And finally the routes.rb
Oneday::Application.routes.draw do
devise_for :users
root 'home#index'
resources :todo
end
Any input as to why this is happening and how to rectify it would be great.
You do not comply with the rails convention. Use plural form for resources. Then, your action is correct.
TodosController, todos_controller.rb, resources :todos
( Rails use singular/plural format to support RESTful links and to recognize named actions )
This
<%= form_for #todo, :url=>todo_path(#todo) do |f| %>
will set (or leave) the form http method to get. You could change it to:
<%= form_for #todo, :url=>todo_path(#todo), method: :post do |f| %>
or even shorter, leave it to Rails to find out what method is needed:
<%= form_for #todo do |f| %>
I found a fix to this exact issue if anyone is still curious, i know its an old issue and an easy one at that, but still figured id solve it. the original route todo_path leads to todo#show. todo_index however is assigned to todo#index and todo#create so its what we want. the line should look like this:
<%= form_for #todo, :url => todo_index_url(#todo), method: :post do |f| %>
I encountered a similar issue with one of my applications and stumbled across this post as a fix. None of the suggestions worked for me, but i was able to fix it with a little tinkering on the routes.

Resources