I have been struggling for 3 hours as I have added mode: range in the Stimulus JS datepicker controller which works on display.
On the logic though, I do not know how to reduce from two to one simple form input since on my bookings table, I have a start_date and end_date columns and I don't know how to extract the date range to assign it correctly.
My booking_form partial:
<div class="row justify-content-center mt-7">
<div class="col-6 mb-3 form-inline justify-content-center" data-controller="datepicker"
data-datepicker-price-value="<%= @room.price_per_night.to_json %>">
<%= simple_form_for ([ @room, @booking]) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div data-controller="datepicker">
<%= f.input :date_range, as: :string, input_html: { data: { datepicker_target: "range", action: "change->datepicker#dateRangeInput" } } %>
</div>
<div class="d-flex col-9 justify-content-between flex-grow-1">
<p> Total price: </p>
<strong data-datepicker-target="price"></strong>
</div>
<div class="col-9 d-flex justify-content-center" >
<%= f.submit "Let's go!", class: "btn btn-primary" , disabled: true, data: { datepicker_target: "confirm" } %>
</div>
<% end %>
</div>
</div>
As shown, I am using only one input field with the :date_range but get an expected
undefined method `date_range' for #<Booking id: nil, user_id: nil, room_id: nil, starts_at: nil, ends_at: nil, created_at: nil, updated_at: nil>
My Stimulus JS controller:
import { Controller } from "@hotwired/stimulus"
import flatpickr from "flatpickr";
// Connects to data-controller="datepicker"
export default class extends Controller {
static values = {
dates: Array,
price: Number
};
static targets = ["range", "confirm", "price"]
connect() {
console.log("datepicker active")
console.log(this.rangeTarget);
this.endTarget.disabled = true
flatpickr(this.startTarget, {
dateFormat: "d F Y",
disable: this.datesValue,
})
}
DateInput() {
flatpickr(this.endTarget, {
mode: "range",
dateFormat: "d F Y",
disable: this.datesValue,
minDate: this.startTarget.value,
minDate: "today"
})
}
endDateInput() {
let splitStartDate = this.startTarget.value.split("-")
let splitEndDate = this.endTarget.value.split("-")
let startDate = new Date([splitStartDate[1], splitStartDate[0], splitStartDate[2]].join("/"))
let endDate = new Date([splitEndDate[1], splitEndDate[0], splitEndDate[2]].join("/"))
let price = parseInt((endDate - startDate) / (1000 * 60 * 60 * 24), 10) * Number(this.priceValue)
this.priceTarget.innerText = `${price}`
console.log(price)
this.confirmTarget.disabled = false
}
}
Sorry for the mess due to some trial and errors.
Finally, my bookings controller:
class BookingsController < ApplicationController
before_action :select_room, only: %i[new create edit]
before_action :select_booking, only: %i[edit update destroy]
def new
@booking = Booking.new
end
def create
@booking = Booking.new(booking_params)
@booking.room = @room
@booking.user = current_user
start_date, end_date = params[:booking][:date_range].split(' to ')
@booking.starts_at = Date.parse(start_date)
@booking.ends_at = Date.parse(end_date)
if @booking.save
redirect_to room_path(@room)
else
render 'rooms/show', status: :unprocessable_entity
end
end
def destroy
@booking.destroy
redirect_to pages_dashboard_path
end
private
def select_room
@room = Room.find(params[:room_id])
end
def select_booking
@booking = Booking.find(params[:id])
end
def booking_params
params.require(:booking).permit(:starts_at, :ends_at)
end
end
I have tried to change the simple form input which generated an error, then tried to change the controller to handle this change, all in order to implement the date range which only requires one field.
Your form HTML and stimulus controller should be like code below:
_form.html.erb
stimulus_controller.js
Please note that the input field class name should match the stimulus flatpickr('.class-name') which is date-range in this case.