undefined method `map' for "MD":String - ruby-on-rails

I'm struggling to create a dynamic dropdown box arrangement. Thanks to StackExchange Ruby community, I was able to create the first dropdown box. What I'd like to do, is have the user select a group of banks in a particular state, but keep getting the following error message from my f.grouped_collection_select form: undefined method `map' for "MD":String. I looked at ActionView::Helpers::Forms and this video, but nothing seems to work. Any help you could provide would be greatly appreciated. I think I also have to add jquery code too? Here is my current view code:
<%= form_for #boli do |f| %>
<%= f.label :state %>
<%= f.collection_select :state, (Boli.order(:state).select("DISTINCT ON (state) id, state")), :id, :state, include_blank: true %>
<div>
<%= f.label :bank %>
<%= f.grouped_collection_select :bank, Boli.order(:bank), :state, :name, :id, :name, include_blank: true %>
</div>
<% end %>

You need to group banks by state. This implies that there is either a one-to-many (one state to many banks) or a many-to-many relationship between them.
The one-to-many relationship implies Bank is a model that either has state as an attribute or references another model State.
The many-to-many relationship implies that both Bank and State are models joined by another table.
With both bank and state as attributes of another model it's unfeasible to model the relationship between them.
In order to use grouped_collection_select you should have both as models.

Related

How to save data to multiple tables in Rails

I am using Devise for authentication in my app and got a model named as "Users". I created an another model for Company informations. I generated the migration with references and everything is okay.
I want to add user informations to users table and company informations to companies table in same registration view.
Also both users and companies tables got the same field as "name".
I tried to specify it as ;
<%= f.text_field :company["name"], autocomplete: "company_name" %>
<%= f.text_field :company[:name], autocomplete: "company_name" %>
<%= f.text_field :companies[:name], autocomplete: "company_name" %>
And i got the errors ;
undefined method `' for #<User:0x00007fd5588cb588>
no implicit conversion of Symbol into Integer
I think somehow i should override devise_controller but i want to learn the general practice for these kind of situations.
Sum: I want to save data to different tables which has same column names in database.
Assuming user belongs_to company and you want to save user and their associated company from one form submit, you can use fields_for for this
<%= f.fields_for :company do |cf| %>
<%= cf.text_field :name %>
<% end %>
This will require accepts_nested_attributes_for :company in User.

Ransack search multiple classes

I have a working search form built using Ransack with two separate search fields for two different Classes like so:
<%= search_form_for #q do |f| %>
<%= f.label :tags_id_in, 'Tags' %>
<%= f.select :tags_id_in, Tag.all.map{ |u| [u.name, u.id] }, { include_blank: "Tags" } %>
<%= f.label :sector_id_eq, 'Sector' %>
<%= f.select :sector_id_eq, Sector.all.map { |w| [w.name, w.id] }, {include_blank: 'Any'} %>
<%= f.submit "Search" %>
<% end %>
Each of these Classes are linked to a Company Class
class Company < ApplicationRecord
belongs_to :sector
has_many :company_tags
has_many :tags, through: :company_tags
accepts_nested_attributes_for :company_tags
end
I am trying to combine the two select fields into one. So far I can build the view for this like so:
<% combined = Sector.all + Tag.all %>
<%= f.select :combined, combined.map { |w| [w.name, w.id] }.sort, {include_blank: 'Any'} %>
Whilst the above works at displaying a single search form, there is no functionality to it. Can anyone help with this? Thanks in advance.
Hooray, Ransack. It seems like this answer from 2013 is still valid:
Search multiple models at once with Ransack
But if you're adventurous, maybe try this:
https://github.com/activerecord-hackery/ransack/issues/131
You can add associations' columns/ransackers to the list of attributes by passing their names to #attribute_select via the :associations option.
I'm not familiar with ransack, but maybe it's worth creating a new stand-in class? Just a PORO (plain ol' ruby object) go-between for the view and the model, using ransack for your queries rather than calling them directly from the form. You could build the tag list in that object as well.
You've got a lot of logic in your view, many frown on putting ActiveRecord queries there. A form object would fix that, and keep your controller simple as well.

Rails Multiple Input Field in Form to One Integer Attribute in Model

I am trying to allow a user to input two different things in two different drop down menus from the same form and it will store an integer into a review table.
I want the user to be able to select model_name in one drop down and manufacturer in another drop down. The result will store a bat_id integer into the form. (Telling you which bat the user is selecting)
I have seen a couple questions about date & time but they store the values directly in the model. I am trying to store an integer - bat_id so that the bat_id will directly link the review model to the bat model.
Examples I have found that are close:
How do ruby on rails multi parameter attributes really work (datetime_select)
Rails multiple fields to one model attribute
Using multiple input fields for one attribute
Rails Update Single Attribute with Multiple Fields
My form now:
<%= form_for(#review) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field" align= "center">
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
<h3>What do you like about this bat?</h3>
<%= f.text_area :pros, placeholder: "Enter what you like..." %>
<h3>What do you not like about this bat?</h3>
<%= f.text_area :cons, placeholder: "Enter what you don't like..." %></br>
</div>
<div align="center">
<%= f.submit "Add Review", class: "btn btn-large btn-info" %>
</div>
<% end %>
I am submitting to the review table and trying to submit both of these to the bat_id attribute.
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.grouped_collection_select :bat_id, Manufacturer.all, :bats, :manufacturer, :id, :model_year_and_name, include_blank: true %>
In my bat model I have: has_many :reviews & In my reviews model I have: belongs_to :bat
UPDATE: Is it possible to use a hidden field with the combination of javascript and my two inputs to determine my one output bat_id?
Update I changed my dropdown code to what works so that I enter in manufacturer_id & bat_id when both are selected. However I still think there is a way to store one value in my review model. I am using javascript very similiar to this
From a UI perspective this seems broken... users will be able to associate any model year & name with any manufacturer, even if that manufacturer did not produce that model year & name.
Assuming you will introduce some javascript to handle that, from a rails perspective you will get undefined behavior with two :bat_id fields in the same form. I think you need this:
<h3>Select Brand</h3>
<%= f.collection_select :manufacturer_id, Manufacturer.all, :id, :manufacturer, include_blank: true %>
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all, :id, :model_year_and_name, include_blank: true %>
Alternatively you can just create one dropdown containing a composite field, like this:
<h3>Select Bat</h3>
<%= f.collection_select :bat_id, Bat.all.sort {|a, b| a.manufacturer_model_year_and_name <=> b.manufacturer_model_year_and_name}, :id, :manufacturer_model_year_and_name, include_blank: true %>
and then in your Bat model introduce something like this:
def manufacturer_model_year_and_name
"#{self.manufacturer.name}: #{self.model_year_and_name}"
end
As discussed in your other answer, you shouldn't need to store the manufacturer_id on your review model.
I would recommend creating a Manufacturer select that isn't accessed in your Review model, but is simply used to filter the list of bats on the form.
The best way to do this is probably to add some custom data attributes to the Bat select.
<%= collection_select :manufacturer, :manufacturer_id, Manufacturer.all, :id, :manufacturer %>
<%= f.select :bat_id, Bat.all.map{ |b| [b.model_year_and_name, b.id, {'data-manufacturer' => b.manufacturer_id}] } %>
Then use some javascript to filter the Bat select when the Manufacturer select is changed.
Unfortunately you cannot just set display: none to an option element to hide it. This does not hide the option in many browsers. So the best method is to use a bit of jQuery to clone the original select every time the manufacturer select is changed, and remove any option that isn't associated with the selected manufacturer. Like so:
// rename the original select and hide it
$('#bat_id').attr('id', 'bat_id_original').hide();
$('#manufacturer_id').on('change', function() {
$('#bat_id').remove(); // remove any bat_id selects
$bat = $('#bat_id_original')
.clone() // clone the original
.attr('id', 'bat_id') // change the ID to the proper id
.insertAfter('#bat_id_original') // place it
.show() // show it
.find(':not(option[data-manufacturer="' + $(this).val() + '"])')
.remove(); // find all options by other manufacturers and remove them
});
You might need to change a few things to get this to work in your installation, but you can view a static demo on jsFiddle here: http://jsfiddle.net/JL6M5/
You will probably need to reject the manufacturer_id field on form submit, avitevet already pointed out this answer which should help there: Rails: Ignoring non-existant attributes passed to create()

Rails Year Make Model Dynamic Menus

Calling all Rails superheroes! I'm racking my brains with this one and can't find a solution anywhere online...I want to build year/make/model drop-downs to select a car from a table of 30,000+ different cars. The table is called car_variants. Here's a link to a similar thing on kbb.org
The Year is selected first which then populates the Make drop-down with those that were available in that year. This in turn populates the Model drop-down. All this filtering overcomes the dreadful problem of having the 30,000 car_variants in one unusable drop-down.
Unfortunately I don't have enough reputation to post my entity diagram, but essentially...
a car_make has many car_models
a car_model has many car_variants
a car_year has many car_variants
SIDE NOTE Since the car_variants table will be populated by importing an excel spreadsheet, I decided to join car_year to car_variants directly rather than saying a car_year has many car_models. That may or may not affect the challenge below.
The car_variants model ( a table of virtually every car since 1984) stores the car_year_id and the car_model_id. (car_make is obvious through car_model)
Now... along comes the user. They want to select their car from the car_variants table then add their car's nickname and license_plate. The user potentially has many cars so the car_variant_id's can't be stored in the user table. Instead we have a car table which contains the users cars.
a car_variant has many cars (think of a car as being like an instance of a car_variant)
a user has many cars
Ryan Bates did an excellent screencast about Dynamic Select Menus. In his example (Countries and States), he used grouped_collection_select and some javascript. That certainly appears to be heading in the right direction for this challenge too, but this is more complex because there are more has_many relationships than grouped_collection_select seems to be able to handle.
I have successfully coded the car form with 3 drop-downs for year, make and model...
<%= simple_form_for(#car) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<!-- select year to filter make dropdown -->
<p>Year</p>
<%= select_tag :car_year, options_for_select(CarYear.all.collect {|e| ["#{e.year}", e.id] }), :include_blank => true %>
<!-- Select make to filter model dropdown -->
<p>Make</p>
<%= select_tag :car_make, options_for_select(CarMake.all.collect {|e| ["#{e.name}", e.id] }), :include_blank => true %>
<!-- Select model -->
<p>Model (grouped collection select)</p>
<%= f.grouped_collection_select :car_variant_id, CarMake.order(:name), :car_models, :name, :id, :name, include_blank: true %>
<%= f.input :nickname, :hint => "If you've given your car a nickname enter it here, otherwise just leave it blank." %>
<%= f.input :plate %>
<%= f.hidden_field :user_id, :value => current_user.id %>
</div>
<div class="form-actions">
<%= f.button :submit, "Add Car" %>
</div>
<% end %>
...now how do I tie them all together so they filter one another then return the car_variant_id to be stored in the car_form.
After several late bleary nights, I'm out of ideas.

Creating Variable Number of one Model instances in one Form

I've been trying to configure a submit form where users can add multiple records to a database at once by adding extra fields with jQuery. (users with names and emails)
But it submits only a single row of data and ignores the rest.
Any advice would be greatly appreciated. Thanks.
<%= semantic_form_for target.users.new, :url => resource_url, :remote => true do |f| %>
<%= f.inputs do %>
<%= f.input :email%>
<%= f.input :name%>
<%= f.input :email, :object =>target.users.new%>#??????? - Here is problem.
<%end%>
<%= f.buttons %>
<%end%>
target - this is parent object
If email is a field, then your users will type several email addresses into a single input field, and you don't need the :object => target.users.new bit at all. You could serialize this as an array before saving to the db - but my guess is what you are trying to do is add an email object - ie you have a User model which has_many :emails - in which case you need to use accepts_nested_attributes :email in the User model and :fields_for :email in the view

Resources