I have two models:
class Location < ActiveRecord::Base
has_many :cows
accepts_nested_attributes_for :cows
end
class Cow < ActiveRecord::Base
belongs_to :location
end
Every location has an id, a name and three boolean values. A cow consists of an id, two strings and two dates and it also includes a location_id.
In my cow view I have a dropdown of every location. It gets updated automatically whenever I create, edit or delete a location.
Below the dropdown you can see all cows ever created. Afer that there is a form to add a new cow with the two string fields and two date fields.
Now I want two things: Whenever I select a value in the dropdown, the displayed cows should change. If I select for example location 2, only cows that have a location_id = 2 in the table should be shown. Also when I create a new cow, the id of the selected location should be saved as well (in this example location_id = 2 should be saved in the cow row when I click the button at the very bottom).
This is how the dynamic dropdown looks like in the cow index.html file:
<%= simple_form_for Cow.new do |f| %>
<%= f.collection_select :location, Location.order('name').all, :id, :name, { prompt: "Ort auswählen" } %>
<% end %>
And this is my cow controller:
def index
if (params[:location] && Location.all.collect(&:name).include?(params[:location][:name]))
@cows = Cow.send(params[:location][:name].downcase)
else
@cows = Cow.all
end
end
# (...)
def create
@cow = Cow.new(cow_params)
# (...)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cow
@cow = Cow.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cow_params
params.require(:cow).permit(:ohrmarke, :hin, :weg, :stallnummer, :location_attributes => [:id])
end
end
The edited index part just does not work. Perhaps because I have a dropdown for Cow.new and I don't have a submit button. Now the thing is: I need the dropdown (value) for a new cow but also want to display cows that fit to the selected dropdown value. Also I don't want to use a submit button just for the dropdown box. Is there any solution for my problem? I googled so much but just can't find the right answer for my problem.
Edit: I edited my cows controller and my index page: cow controller:
def index
@locations_all = Location.all
if (params[:location] && cows = Cow.where(location_id: params[:location]))
@cows = cows
@location = Location.where(id: params[:location])
else
@cows = Cow.all
end
end
# (...)
def create
@cow = Cow.new(cow_params)
# (...)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cow
@cow = Cow.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cow_params
params.require(:cow).permit(:ohrmarke, :hin, :weg, :stallnummer, :location_attributes => [:id])
end
cow index.html:
<%= simple_form_for Cow.new do |f| %>
<%= f.collection_select :location, Location.order('name').all, :id, :name, { prompt: "Ort auswählen" } %>
<ul>
<% @locations_all.each do |loc| %>
<li><%= link_to loc.name, @cows.index(location: loc.id) %></li>
<% end %>
</ul>
<%= f.hidden_field :location_id, value: @location_id %>
<table class="striped">
<thead class="thead-default">
<tr>
<th>Ohrmarke</th>
<th>Stallnr.</th>
<th>Hin/Weg</th>
</tr>
</thead>
<tbody>
<!-- ... -->
</tbody>
</table><br>
<%= f.input :ohrmarke, as: :string, input_html: { maxlength: 5 } %>
<%= f.input :stallnummer, as: :string, input_html: { maxlength: 3 } %>
<!-- datepicker not functional yet -->
<input type="date" class="datepicker" id="hin" placeholder="hin">
<input type="date" class="datepicker" id="hin" placeholder="weg">
<button class="btn waves-effect waves-light" type="submit" name="action">Zuordnung speichern
<i class="material-icons right">send</i>
</button>
<% end %>
Drop-down & Content
This is a possible implementation for cows_controller#index
.
def index
@locations_all = Location.all
if (params[:location] && cows = Cow.where(location_id: params[:location]))
@cows = cows
@location = Location.where(id: params[:location])
else
@cows = Cow.all
end
end
There are however a few important points to be made and a few problems.
The code you're using for the drop-down assumes you're making a POST request which isn't necessary in this case. The same can be achieved with a simple link_to
. In order for this to work, you'd have something similar to this:
<ul>
<% @locations_all.each do |loc| %>
<li><%= link_to loc.name, cows_path(location: loc.id) %></li>
<% end %>
</ul>
Form
In order for the form to create a new Cow
using the correct location you'd need to include a hidden_field
inside it.
<%= f.hidden_field :location_id, value: @location.id %>
But this introduces a design problem. What happens when you haven't selected any location
on the drop-down menu? There are plenty of ways to handle this problem, but my favorite solution is to hide the form in that case.
<% unless @location %>
#form-block ...
<% end %>
This solutions seems ideal if cows
can't be created without a specified location
.
Ok, now that I can see you HTML. I can tell you what wrong. Your simple_form_for
block is surrounding the whole view. Which is probably not what you want. forms
are usually POST requests, so you only need those when you plan to send data, or create a new entry on a table. link_to
is a GET request which you can request information to the server. This a small example of how my view would look like:
#drop-down menu
<div id="cow_menu">
<ul>
<% @locations_all.each do |loc| %>
<li><%= link_to loc.name, cows_path(location: loc.id) %></li>
<% end %>
</ul>
</div>
#table
<table class="striped">
# your table logic...
</table>
#new Cow form
<%= simple_form_for Cow.new do |f| %>
<%= f.hidden_field :location_id, value: @location_id %>
<%= f.input :ohrmarke, as: :string, input_html: { maxlength: 5 } %>
<%= f.input :stallnummer, as: :string, input_html: { maxlength: 3 } %>
<%= f.submit "Zuordnung speichern", class="btn waves-effect waves-light"%>
<% end %>