Rails form - Iterating over collection in form - ruby-on-rails

In my form I want to iterate over a specific collection and collect the same information about each. For simplicity's sake something like:
<%= form_tag :update_dog do %>
<% #dogs.each do |dog| %>
<%= text_field_tag :name, :class=>dog.id %>
<% end %>
<%= submit_tag "Add", :class => 'btn btn-success'%>
<%= end %>
Where I would want to collect each dog's name to manipulate in the controller (in which I'd like to be able to iterate over each submitted dog name and access its id). The potential number of dogs in my collection is variable. What is the best way to do this? The code above is what I have so far but I have no idea if it's right and if so, how to use it in the controller.
Thanks so much!

I'd start with a filter. Create a before_filter that creates your dogs
class KennelController < ApplicationController
before_filter :get_dogs , :only=>[:new,:edit]
def get_dogs
#dogs = Dog.all.map{|d| [d.name, d.id]}
end
....
end
Then in either your new or edit views, you could do this:
<%= select_tag :dog, options_for_select(#dogs) %>

Related

Rails form not creating object

I have created a simple form to create an instance of a modle and for some reason it is not calling the create method in the controller. Here is the form code:
<% #house.mates.each do |mate| %>
<p><%= mate.name %></p>
<% end %>
<h2>Add a new mate:</h2>
<%= form_for #mate do |f| %>
<p><%= f.label "Name" %>
<%= f.text_field :name %>
<%= f.hidden_field :house_id %>
</p>
<%= f.submit "Submit", :action => :create %>
<% end %>
Here is the controller code:
class MatesController < ApplicationController
def new
#mate = Mate.new
end
def create
#mate = Mate.new(params[:mate])
#mate.save
redirect_to house_path(current_house)
end
end
There is a many to one relationship between the Mate model and the House model... I am fairly new to rails but I have made other apps with similar forms, and I have never had this problem before. I can create and save Mate objects in the console, and I am not getting any errors, so it seem that somehow the controller method is not being called. Any help is much appreciated!
In fact, if other things have no problem, your #mate object should be created. You just can't see it in house page because you have not associated #mate with house in your code.
In your form you referred :house_id, but this attribute is nil when you rendering the form.
The reason is you have not assigned it in controller.
In controller you need to initialize #mate from house object to have house_id inside it
def new
#house = something
#mate = #house.mates.new # Instead of Mate.new
end

checkboxtag in forms

Im looking for the following thing: an array of all users (only 6 in this case) with a checkbox in front of their name, resulting in a list of selectable players for the game.
Current code:
<%= form_for #game, url: games_path, :method => "post" do |f| %>
<%= f.label :name %>
<%= f.text_field :name, :value => "#{current_user.name}\'s Game" %>
<%= f.fields_for :participants do |ff| %>
<%= ff.label :user_id %>
<%= ff.text_field :user_id %>
<%= ff.check_box :user_id %>
<% end %>
<%= f.submit "Create Game", class: "btn btn-primary" %>
<% end %>
I'm now having 3.times { #game.participants.build } in my controller which effectively gives me 3 textfields in which i can fill in the participant id in order to make a record in the table participants (which is linked to games).
I've been looking around for 1.5h now and i cant seem to find a proper answer. What i need is a syntax that gives me a list of all current users (say #users) with a checkbox attached to it. When I click the checkbox it should add its id to the parameters and i should be able to create a new game with the linked participant id's. However I'm getting some problems with the ID's attached to the check_box which always seems to be 1. I've read some stuff about checkboxes being a pain with hashes, but I have no other solution atm.
I tried:
<% #users.each do |i| %>
<%= check_box_tag "alternate_numbers[#{i}]" %> <%= i.name %><br />
<% end %>
But i see no way to get that fixed up part of the form itself.
GamesController code (edit):
def new
#users = User.paginate(page: params[:page])
#games = current_user.games
#game = Game.new
3.times { #game.participants.build }
end
def create
#game = Game.new(params[:game])
#newround = #game.rounds.new
#newround.storyFragment = "New story!"
if #game.save && #newround.save
flash[:success] = "Created!"
redirect_to game_path(#game.id)
else
redirect_to root_url
end
end
It's very vague to describe since im not exactly sure how to accomplish this.
In short: the check_box should contain the value of the user_id in the loop. I'm now filling in a manual ID with the text_field helper but i'd like to have the checkbox linked to the username that is right next to it in the view.
Any guidelines/solutions/tips?
Thx
Okay, so you're making a form for a new Game. You now have to feed that new Game, along with some Participants to your view.
def new
#game = Game.new
#participants = User.all # or the users you want
end
Now use those in your view. You were on the right track. Depending on how your create action works:
<% #participants.each do |p| %>
<%= check_box_tag "participants[#{p.id}]" %> <%= p.name %>
<% end %>
I think what you were missing was the documentation for check_box_tag. The input attribute name is the argument.
You also seem to have a lot of logic in your controllers. Remember to keep the logic in the models, and only use the controllers to give the right objects to your views, and taking them for saving, for example. As the saying goes, "fat model, skinny controller".

How can I display a list of three different Models sortable by the same :attribute in rails?

I have a Campaign model which has_many Calls, Emails, and Letters.
For now, these are each a separate Model with different controllers and actions (although I would like to start to think of ways to collapse them once the models and actions stabilize).
They do share two attributes at least: :days and :title
I would like a way to represent all the Calls, Emails, and Letters that belong_to a specific Campaign as a sortable collection (sortable by :days), in a way that outputs the model name and the path_to() for each.
For example (I know the below is not correct, but it represents the kind of output/format I've been trying to do:
#campaign_events.each do |campaign_event|
<%= campaign_event.model_name %>
<%= link_to campaign_event.title, #{model_name}_path(campaign_event) %>
end
Thanks so much. BTW, if this matters, I would then want to make the :days attribute editable_in_place.
Here is what I've got working, but want some additional insights
module CampaignsHelper
def campaign_events
return (#campaign.calls + #campaign.emails + #campaign.letters).sort{|a,b| a.days <=> b.days}
end
end
In the VIEW:
<% #campaign_events = campaign_events %>
<% #campaign_events.each do |campaign_event| %>
<% model_name = campaign_event.class.name.tableize.singularize %>
<p>
<%= link_to campaign_event.title, send("#{model_name}_path", campaign_event) %>
<%= campaign_event.days %>
</p>
<% end %>
Like this?
# controller
#campaign = Campaign.find(params[:id])
#campaign_events = (#campaign.calls + #campaign.emails + #campaign.letters).sort{|a,b| a.days <=> b.days}
# view
#campaign_events.each do |campaign_event|
<%= campaign_event.model_name %>
<%= link_to campaign_event.title, #{model_name}_path(campaign_event) %>
end
In controller you find all campaign events and sort it by days field

Passing Form Values into a controller in Rails

say I have a text field like the following in a view called 'search':
<%= text_field_tag(:lookup) %>
how do I submit this ':lookup' value and pass it into the controller called 'search' and assign it to a variable?
It's a basic problem, but being a noob, it's difficult ;)
That will be accessible in the controller as
params[:lookup]
Your controller could look something like this:
class SearchesController < ActionController::Base
def search
lookup = params[:lookup]
#models = Model.find_by_lookup(lookup)
end
end
And your view should look like this:
<%= form_tag searches_path do %>
<label for="lookup">Lookup</label>
<%= text_field_tag :lookup %>
<%= submit_tag "Submit" %>
<% end %>

Multiple objects in a Rails form

I want to edit multiple items of my model photo in one form. I am unsure of how to correctly present and POST this with a form, as well as how to gather the items in the update action in the controller.
This is what I want:
<form>
<input name="photos[1][title]" value="Photo with id 1" />
<input name="photos[2][title]" value="Photo with id 2" />
<input name="photos[3][title]" value="Custom title" />
</form>
The parameters are just an example, like I stated above: I am not sure of the best way to POST these values in this form.
In the controller I want to something like this:
#photos = Photo.find( params[photos] )
#photos.each do |photo|
photo.update_attributes!(params[:photos][photo] )
end
In Rails 4, just this
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[]", photo do |pf| %>
<%= pf.text_field :caption %>
... other photo fields
UPDATE: This answer applies to Rails 2, or if you have special constraints that require custom logic. The easy cases are well addressed using fields_for as discussed elsewhere.
Rails isn't going to help you out a lot to do this. It goes against the standard view conventions, so you'll have to do workarounds in the view, the controller, even the routes. That's no fun.
The key resources on dealing with multi-model forms the Rails way are Stephen Chu's params-foo series, or if you're on Rails 2.3, check out Nested Object Forms
It becomes much easier if you define some kind of singular resource that you are editing, like a Photoset. A Photoset could be a real, ActiveRecord type of model or it can just be a facade that accepts data and throws errors as if it were an ActiveRecord model.
Now you can write a view form somewhat like this:
<%= form_for :photoset do |f|%>
<% f.object.photos.each do |photo| %>
<%= f.fields_for photo do |photo_form| %>
<%= photo_form.text_field :caption %>
<%= photo_form.label :caption %>
<%= photo_form.file_field :attached %>
<% end %>
<% end %>
<% end %>
Your model should validate each child Photo that comes in and aggregate their errors. You may want to check out a good article on how to include Validations in any class. It could look something like this:
class Photoset
include ActiveRecord::Validations
attr_accessor :photos
validate :all_photos_okay
def all_photos_okay
photos.each do |photo|
errors.add photo.errors unless photo.valid?
end
end
def save
photos.all?(&:save)
end
def photos=(incoming_data)
incoming_data.each do |incoming|
if incoming.respond_to? :attributes
#photos << incoming unless #photos.include? incoming
else
if incoming[:id]
target = #photos.select { |t| t.id == incoming[:id] }
end
if target
target.attributes = incoming
else
#photos << Photo.new incoming
end
end
end
end
def photos
# your photo-find logic here
#photos || Photo.find :all
end
end
By using a facade model for the Photoset, you can keep your controller and view logic simple and straightforward, reserving the most complex code for a dedicated model. This code probably won't run out of the box, but hopefully it will give you some ideas and point you in the right direction to resolve your question.
Rails does have a way to do this - I don't know when it was introduced, but it's basically described here: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers
It took a bit of fiddling to alter the configuration properly for the case where there's no parent object, but this seems to be correct (it's basically the same as gamov's answer, but cleaner and doesn't allow for "new" records mixed in with the "update" records):
<%= form_tag photos_update_path do %>
<% #photos.each do |photo| %>
<%= fields_for "photos[#{photo.id}]", photo do |pf| %>
<%= pf.text_field :caption %>
... [other fields]
<% end %>
<% end %>
<% end %>
In your controller, you'll end up with a hash in params[:photos], where the keys are photo IDs, and the values are attribute hashes.
You can use "model name[]" syntax to represent multiple objects.
In view, use "photo[]" as a model name.
<% form_for "photo[]", :url => photos_update_path do |f| %>
<% for #photo in #photos %>
<%= render :partial => "photo_form", :locals => {f => f} %>
<%= submit_tag "Save"%>
<% end %>
<% end %>
This will populate input fields just like you described.
In your controller, you can do bulk updates.
def update
Photo.update(params[:photo].keys, params[:photo].values)
...
end
Indeed, as Turadg mentioned, Rack (Rails 3.0.5) fails if you mix new & existing records in Glen's answer.
You can work around this by making fields_for work manually:
<%= form_tag photos_update_path do %>
<% #photos.each_with_index do |photo,i| %>
<%= fields_for 'photos[#{i}]', photo do |pf| %>
<%= pf.hidden_field :id %>
... [other photo fields]
<% end %>
<% end %>
This is pretty ugly if you ask me, but it's the only way I found to edit multiple records while mixing new and existing records.
The trick here is that instead of having an array of records, the params hash gets a array of hashes (numbered with i, 0,1,2, etc) AND the id in the record hash. Rails will update the existing records accordingly and create the new ones.
One more note: You still need to process the new and existing records in the controller separately (check if :id.present?)

Resources