I have cascading 3 dropdown lists. User can select Model Brand and Year and it saves to Boat Model. User has many boats. Boat belongs to User.
The problem is with #create
action i guess. Because it saves ids instead of the names to Boat Model (Brand Model Year (all string)).
Here is my controller;
def new
@boat = Boat.new
end
def create
@boat = current_user.boats.new(boat_params) if logged_in?
if @boat.save
flash[:success] = "Boat created!"
render 'edit'
else
render 'new'
end
end
def show
end
def edit
@boat = Boat.find(params[:id])
end
def update
end
def update_years
# updates year and model based on brand selected
brand = Brand.find(params[:brand_id])
# map to name and id for use in our options_for_select
@years = brand.years.map{|a| [a.name, a.id]}.insert(0, "Select a Year")
@models = brand.models.map{|s| [s.name, s.id]}.insert(0, "Select a Model")
end
def update_models
# updates model based on year selected
year = Year.find(params[:year_id])
@models = year.models.map{|s| [s.name, s.id]}.insert(0, "Select a Model")
end
end
private
def boat_params
params.require(:boat).permit(:brand, :year, :model)
end
Here is my #new
view;
<% provide(:title, 'List My Boat') %>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@boat) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="col-md-6">
<%= f.label :Brand %>
<%= f.collection_select(:brand, Brand.all, :id, :name, {:prompt => "Select a Brand"}, {:id => 'brands_select'}) %>
</div>
<div class="col-md-6">
<%= f.label :Year %>
<%= f.collection_select(:year, Year.all, :id, :name, {:prompt => "Select a Year"}, {:id => 'years_select'}) %>
</div>
<div class="col-md-6">
<%= f.label :Model %>
<%= f.collection_select(:model, Model.all, :id, :name, {:prompt => "Select a Model"}, {:id => 'models_select'}) %>
</div>
<div class="col-md-6 col-md-offset-3">
<%= f.submit "Next", class: "btn btn-primary"%>
</div>
<% end %>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('#brands_select').change(function() {
$.ajax({
url: "<%= update_years_path %>",
data: {
brand_id : $('#brands_select').val()
},
dataType: "script"
});
});
$('#years_select').change(function() {
$.ajax({
url: "<%= update_models_path %>",
data: {
year_id : $('#years_select').val()
},
dataType: "script"
});
});
});
</script>
Lastly, if I change the id here
<%= f.collection_select(:brand, Brand.all, :id, :name, {:prompt => "Select a Brand"}, {:id => 'brands_select'}) %>
to name, then cascading property does not work. So the form does not update itself dynamically.
I have tried something like at #create
action
year = Year.find_by("id = ?", params[:year])
model = Year.find_by("id = ?", params[:model])
brand = Year.find_by("id = ?", params[:brand])
@boat = current_user.boats.new(:year, year.name, :brand, brand.name, :model, model.name)
But then it gives an error because of @boat = current_user.boats.new(:year, year.name, :brand, brand.name, :model, model.name)
is wrong. I receive
undefined method `name' for nil:NilClass
error.
EDIT 1:
update_models.js.erb
$('#models_select').html("<%= escape_javascript(options_for_select(@models)) %>");
update_years.js.erb
$('#years_select').html("<%= escape_javascript(options_for_select(@years)) %>");
$('#models_select').html("<%= escape_javascript(options_for_select(@models)) %>");
If you look at rails collection_select method
, it's the value method that gets passed on as params, in your case it's the id of year and brand.
Change your collection select methods to this:
<%= f.collection_select(:brand, Brand.all, :name, :name, {:prompt => "Select a Brand"}, {:id => 'brands_select'}) %>
<%= f.collection_select(:year, Year.all, :name, :name, {:prompt => "Select a Year"}, {:id => 'years_select'}) %>
Your script should be:
<script>
$(document).ready(function() {
$('#brands_select').change(function() {
$.ajax({
url: "<%= update_years_path %>",
data: {
brand_name : $('#brands_select').val()
},
dataType: "script"
});
});
$('#years_select').change(function() {
$.ajax({
url: "<%= update_models_path %>",
data: {
year_name : $('#years_select').val()
},
dataType: "script"
});
});
});
</script>
and inside your methods you need to find brand and year by name instead of ids
def update_years
# updates year and model based on brand selected
brand = Brand.find_by_name(params[:brand_name])
# map to name and id for use in our options_for_select
@years = brand.years.map{|a| [a.name, a.name]}.insert(0, "Select a Year") #use a.name here instead of a.id
@models = brand.models.map{|s| [s.name, s.name]}.insert(0, "Select a Model")#use s.name here instead of s.id
end
def update_models
# updates model based on year selected
year = Year.find_by_name(params[:year_name])
@models = year.models.map{|s| [s.name, s.name]}.insert(0, "Select a Model") #use s.name here instead of s.id
end