I'm searching for a way to create simple_form fields of all options belonging to a room_type, such that a user can fill in the option_quantity. The room_type is asked first in a form and consequently this input needs to be used for the options belonging to that room_type.
Current attempt
Currently, I'm getting al options belonging to this room_type with JQuery, but I don't know how to proceed from here (or maybe there is a better way?)
Response current attempt
The following response is generate using my current attempt.
{rooms: Array(3), options: Array(2), extra_guests: Array(1)}
rooms: (3) [{…}, {…}, {…}]
extra_guests: [{…}]
options: Array(2)
0: {id: 109, room_type_id: 185, name: "Amazing option", description: "", rank: null, …}
1: {id: 110, room_type_id: 185, name: "Second option", description: "", rank: null, …}
length: 2__proto__: Array(0)__proto__: Object
Using this JSON, is it possible to create a separate option field for each option with a field where the user is asked to fill in the option_quantity?
Code
form
<%= simple_form_for [@hotel, @reservation] do |f|%>
<%= f.simple_fields_for :rooms do |room| %>
<%= room.input :room_type, collection: @room_type_list, input_html:{
id: "room_type"
}%>
<% end %>
<h4>Options</h4>
<!-- List all options for room_type by name and display field for option_quantity -->
<% end %>
<script >
// dynamic options for change category
$(document).on("change", "#room_type", function(){
var room_type = $(this).val();
$.ajax({
url: "/hotels/<%= @hotel.id %>/reservations/new",
method: "GET",
dataType: "json",
data: {room_type: room_type},
error: function (xhr, status, error) {
console.error('AJAX Error: ' + status + error);
},
success: function (response) {
var options = response["options"];
console.log(response);
console.log(options)
console.log($("room_type-options").html(response));
// Code to generate list of options
}
});
});
</script>
schema
create_table "reservation_options", force: :cascade do |t|
t.bigint "option_id"
t.bigint "reservation_id"
t.integer "option_quantity"
[]
t.index ["option_id"], name: "index_reservation_options_on_option_id"
t.index ["reservation_id"], name: "index_reservation_options_on_reservation_id"
end
create_table "options", force: :cascade do |t|
t.bigint "room_type_id"
t.string "name"
[]
t.index ["room_type_id"], name: "index_options_on_room_type_id"
end
reservation controller
class ReservationsController < ApplicationController
# skip_before_action :authenticate_user!
def new
@hotel = Hotel.find(params[:hotel_id])
@reservation = Reservation.new
@room_type_list = @hotel.room_types
@all_options = @hotel.options
@rooms = []
@options = []
@extra_guests = []
# # Display rooms/options for category
if params[:room_type].present?
@rooms = RoomType.find(params[:room_type]).rooms
@options = RoomType.find(params[:room_type]).options
@extra_guests = RoomType.find(params[:room_type]).extra_guests
end
if request.xhr?
respond_to do |format|
format.json {
render json: {rooms: @rooms, options: @options, extra_guests: @extra_guests}
}
end
end
authorize @reservation
end
private
def reservation_params
params.require(:reservation).permit(:arrival, :departure, :payment, :reservation_contact_id, option_ids:[],
reservation_contact_attributes: [:id, :first_name,
:last_name, :first_name, :last_name, :zipcode, :city, :street, :street_number,
:email, :phone, :date_of_birth, :country, :company, :gender, :vat_number],
rooms_attributes: [:id,:name, :room_type_id,
room_types_attributes: [:id, :name]],
reservation_options_attributes: [:id, :option_id, :option_quantity, :_destroy,
options_attributes: [:id, :name, :room_type_id, :description,
room_types_attributes:[:id, :name]]],
reservation_extra_guests_attributes: [:id, :extra_guest_id, :extra_guest_quantity, :_destroy,
extra_guests_attributes: [:id, :name, :room_type_id, :age_table_id,
room_types_attributes:[:id, :name]]])
end
models
class Reservation < ApplicationRecord
belongs_to :hotel
belongs_to :room
has_many :reservation_options, inverse_of: :reservation, dependent: :destroy
accepts_nested_attributes_for :reservation_options
has_many :options, through: :reservation_options
end
class Room < ApplicationRecord
belongs_to :room_type
validates :name, presence: true
has_many :reservations, dependent: :destroy
accepts_nested_attributes_for :room_type
end
class RoomType < ApplicationRecord
belongs_to :hotel
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms, allow_destroy: true
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
end
class Option < ApplicationRecord
belongs_to :room_type
has_many :reservation_options, dependent: :destroy
has_many :option_prices, dependent: :destroy, inverse_of: :option
end
Outputs responses
console.log(response); =>
{rooms: Array(3), options: Array(2), extra_guests: Array(1)}
rooms: (3) [{…}, {…}, {…}]
extra_guests: [{…}]
options: Array(2)
0: {id: 109, room_type_id: 185, name: "Amazing option", description: "", rank: null, …}
1: {id: 110, room_type_id: 185, name: "Second option", description: "", rank: null, …}
length: 2__proto__: Array(0)__proto__: Object
console.log(options); =>
0: {id: 109, room_type_id: 185, name: "Amazing option", description: "", rank: null, …}
1: {id: 110, room_type_id: 185, name: "Second option", description: "", rank: null, …}
length: 2
__proto__: Array(0)
console.log($("#room_type-options").html(response)); =>
jQuery.fn.init {context: document, selector: "#room_type-options"}
First, I think I would make my routes look something like:
Rails.application.routes.draw do
resources :hotels do
resources :room_types, shallow: true do
scope module: :room_types do
resources :options, only: [:index]
end
end
end
end
Which will give you:
room_type_options GET /room_types/:room_type_id/options(.:format) room_types/options#index
hotel_room_types GET /hotels/:hotel_id/room_types(.:format) room_types#index
POST /hotels/:hotel_id/room_types(.:format) room_types#create
new_hotel_room_type GET /hotels/:hotel_id/room_types/new(.:format) room_types#new
edit_room_type GET /room_types/:id/edit(.:format) room_types#edit
room_type GET /room_types/:id(.:format) room_types#show
PATCH /room_types/:id(.:format) room_types#update
PUT /room_types/:id(.:format) room_types#update
DELETE /room_types/:id(.:format) room_types#destroy
hotels GET /hotels(.:format) hotels#index
POST /hotels(.:format) hotels#create
new_hotel GET /hotels/new(.:format) hotels#new
edit_hotel GET /hotels/:id/edit(.:format) hotels#edit
hotel GET /hotels/:id(.:format) hotels#show
PATCH /hotels/:id(.:format) hotels#update
PUT /hotels/:id(.:format) hotels#update
DELETE /hotels/:id(.:format) hotels#destroy
If needed and/or desired, you can limit the generated routes using only:
or except:
. For instance, you could do:
Rails.application.routes.draw do
resources :hotels, only: [] do
resources :room_types, only: [], shallow: true do
scope module: :room_types do
resources :options, only: [:index]
end
end
end
end
And get just:
room_type_options GET /room_types/:room_type_id/options(.:format) room_types/options#index
You could also get the same thing by doing:
Rails.application.routes.draw do
get 'room_types/:room_type_id/options', to: 'room_types/options#index'
end
Whichever floats your boat...
Then, I would change my js to something more like:
<%= simple_form_for [@hotel, @reservation] do |f|%>
<%= f.simple_fields_for :rooms do |room| %>
<%= room.input :room_type, collection: @room_type_list, input_html:{
id: "room_type"
}%>
<% end %>
<h4>Options</h4>
<div id="room-type-options">
<!-- List all options for room_type by name and display field for option_quantity -->
</div>
<% end %>
<script >
// dynamic options for change category
$(document).on("change", "#room_type", function(){
var room_type = $(this).val();
var room_type_id = # do something here...
$.ajax({
url: "/room_types/#{room_type_id}/options",
method: "GET",
dataType: "json",
error: function (xhr, status, error) {
console.error('AJAX Error: ' + status + error);
},
success: function (response) {
console.log(response);
$("#room-type-options").html(response)
}
});
});
</script>
A few things to note here:
RoomTypes::OptionsController
with an index
actionindex
action should return an html blob of room type options hotel_id
because a RoomType belongs_to:hotel
and the hotel_id
, therefore, can be derived (if needed) from the RoomType
(thus the shallow: true
bit). You can read more about this in the Rails Routing from the Outside In guidevar room_type_id =
bit on your own. You should be able to get that from the room.input
but may need to tweak your code a bit - I'm not sure$("#room-type-options").html(response)
bit is directionally correct, but may need to be fiddled with because I forget exactly what the response structure is <div id="room-type-options">
bit may or may not be correct. I haven't hand-crafted html in I don't know how long. (I use haml.) You can implement the RoomTypes::OptionsController
by creating an options_controller.rb
file in a app/controllers/room_types
folder. It would look like any other controller:
# app/controllers/room_types/options_controller.rb
class RoomTypes::OptionsController < ApplicationController
def index
@room_type = RoomType.find_by(params[:room_type_id])
... do some stuff ...
... render some html ...
... do a happy dance ...
end
end
Because this controller is namespaced, you can have it at the same time as you have your regular OptionsController
and RoomTypesController
and there won't be any conflict.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.