While I was thinking of introducing pull-down selection to form_with and implementing it, there were some places where I repeated trial and error, so I will leave it as a reminder.
・ Ruby 2.6.5 ・ Rails 6.0.3
There is a shop table where you can register restaurant information. There is a station table that contains station data for the Yamanote line as a list. The two tables have a many-to-many relationship and form an association via an intermediate table (shop_station table).
I want to implement the following functions. ** ① When creating shop data, I want to select the nearest station from the pull-down menu and register it. ** ** ** ② Registration of the nearest station is optional and does not have to be registered. You can register up to 2 stations. ** ** ** ③ Naturally, I want to register all the nearest stations at once by submitting the form once. ** **
I will leave the above implementation procedure as a reminder.
Well, I will write the procedure below.
This time we will implement the form in the new action of the shop controller. I will extract the code first and write it.
Controller
shops_controller.rb
before_action :set_select_lists, only: [:new]
def new
@shop = Shop.new
end
private
def set_select_lists
@stations = Station.all.map {|station| [station.name, station.id] }
end
View
haml:new.html.haml
.shop-wrapper
= form_with model: [@owner, @shop], html: {class: "shopform"}, local: true do |f|
-#Omission(Indentation is appropriate for syntax highlighting. Excuse me. Indent according to haml notation)
= f.select :station_ids, @stations,{},{class: "select"}
= f.select :station_ids, @stations,{},{class: "select"}
* By registering the nearest station, it will be displayed in the station designation search.
The explanation of each is as follows.
Controller @stations is creating the data that will be the basis for this choice. The following is a rough explanation.
Station.all All data in the Station table
Station.all.map {~} For each data (record) in the Station table all data, process according to {}
Station.all.map {|station| [station.name, station.id]} Generates a hash with each record as station, station.name (value in the name column of each record) as the key, and station.id (value in the id column) as the value.
View
= f.select :station_ids, @stations,{},{class: "select"}
Send data with form_with. Create a pull-down with f.select. When sending data, send it with the key: station_ids. Choices are based on @stations. The class name is select.
The key defined in the controller is ** "choices displayed in the pull-down" **, and the value is ** "value sent by params" **. When people choose, they want to choose by station name, and when registering data, they want to associate by id. That's why I set the key and value like this.
The figure that implements these is as follows.
Two of the same pull-downs are lined up and one is open. Certainly station.name is displayed as an option.
In the image above, since the list is made from table data, ** there is no "choice" of "do not select a station". ** ** I rewrote the controller as follows to make the common "Please choose from the following".
shops_controller.rb
def set_select_lists
@stations = Station.all.map {|station| [station.name, station.id] }.unshift(["Please choose from the following", nil])
end
I added .unshift (["Please choose from below", nil]) to @stations earlier. unshift is a method that adds an element to the very beginning of an array. We added the option to pass nil to the value with the sentence "Please choose from the following" as the key.
Adding a record such as "there is no nearest station" to the table data itself is not preferable because the number of records in the intermediate table will increase unnecessarily, so I added the option to set the value to nil.
Now you have successfully implemented the pull-down you want.
I thought that I could register at this point, but when I actually skipped the data from params, the following problem occurred. ** Since both pull-downs send data with the key with the same name station_ids, the result is that the second value overwrites the first value and only one value is sent. ** **
It was obvious when I looked at binding.pry. However, what I want to do here is to send and register multiple station_ids. That is, I want to send ** station_ids as an array **
I think there are several ways to do it, but I rewrote the view as follows (this is probably a power technique, but I was able to use it in the form after that)
haml:new.html.haml
= f.select :station_ids, @stations,{},{name: 'shop[station_ids][]', class: "select"}
= f.select :station_ids, @stations,{},{name: 'shop[station_ids][]', class: "select"}
The name attribute is added to the previous state. If you add the name attribute in form_with, you can specify ** "How do you send the params?" **.
If you actually look at the params in binding.pry, you can see how the data is sent. station_ids are sent in the form ** params [: shop] [: station_ids] **.
I wanted to make this an array, so by adding [] at the end of the name attribute, I can safely send both data in the form of an array.
shops_controller.rb
def shop_params
params.require(:shop).permit(:name, :address, :capacity, :owner_id, :genre_id, :mark_ids,introduces_attributes: [:content, :image, :number],station_ids: [])
end
By the way, I mentioned earlier that the data was sent in the form of params [: shop] [: station_ids], so as mentioned above, with the strong parameter ** First, require: shop and press the key in it. By permitting, the data will be entered safely. ** ** I understood this well in binding.pry. Was funny.
The method of specifying with the name attribute is quite powerful, so please tell me if you meet a smarter method. However, I implemented each value of the form after this quite a bit.
I learned the most from seeing how params are sent in binding.pry.
Recommended Posts