问题描述
我正在尝试构建一个表格,允许用户预订他们希望参加的武术课。我创建了一个表单,该表单会根据用户所做的选择动态更改,当我更改任何选择选项时,表单都会更新,并且在我提交表单时,它将重定向到Stripe检出。我遇到的问题是提交表单后,我单击了浏览器的“后退”按钮或“条纹”结帐页面上提供的“后退”按钮,已更新的选择选项已还原为默认选项,而不是已更新的选项。谁能帮助我纠正此行为并获取正确的表单元素以持久化? 这是我用来执行此操作的代码: 我在以下位置呈现表单的视图:
<% content_for :banner_title,@page_data['bannerTitle'] %>
<% content_for :head do %>
<Meta name="turbolinks-cache-control" content="no-cache">
<% end %>
<div class="content container py-5">
<div class="row">
<div class="col-12 col-md-7 mx-auto">
<%= render "forms/booking",options: @options %>
</div>
</div>
</div>
我正在使用的表单:
<%= form_for @booking,class: 'booking clearfix' do |f| %>
<div class="form-group">
<%= f.label(:class_name,"Select a class you wish to attend: ") %>
<%= f.select(:class_name,options_for_select(options[:class_names],@booking.class_name),{},class: 'form-control' ) %>
</div>
<div class="form-group">
<%= f.label(:date,"Select a date:") %>
<%= f.select(:date,options_for_select( options[:dates],@booking.date ),class: 'form-control' ) %>
</div>
<div class="form-group">
<%= f.label(:time,"Select a time: ")%>
<%= f.select(:time,options_for_select(options[:times],@booking.time),class: 'form-control') %>
</div>
<div class="form-group">
<%= f.label(:attendees,"How many attending: ") %>
<%= f.select(:attendees,options_for_select(options[:attendees],@booking.attendees),class: 'form-control' )%>
</div>
<%= f.submit 'Place Booking',class: 'btn btn-primary btn-lg text-light float-right',id: 'create-booking' %>
<% end %>
<%= javascript_pack_tag 'booking_form' %>
<script src="https://js.stripe.com/v3/"></script>
表单的模型(我不使用ActiveRecord,我不知道这有什么区别吗?):
class Booking
include ActiveModel::Model
MAX_ATTENDEES = 10
attr_accessor :time,:class_data,:attendees,:date,:class_name
def initialize(args={})
@time = args['time']
@class_name = args['class_name']
@class_data = args['class_data']
@date = args['date']
@attendees = args['attendees']
end
def day
@date.split(',').first
end
def available_dates
days_index_array = class_data['times'].keys.map {|k| day_index(k) }
days_within( days_index_array )
end
def available_times
if !date
class_data['times'][class_data['times'].keys.first.downcase]
else
class_data['times'][day.downcase]
end
end
def total_cost
@class_data['cost'].to_i * @attendees.to_i
end
def attending_string
ActionController::Base.helpers.pluralize(attendees,'person')
end
private
def days_within(days,timeframe=1.month)
start_date = Date.tomorrow
end_date = start_date + timeframe
(start_date..end_date).to_a.select {|k| days.include?(k.wday) }
end
def day_index(day)
DateTime::DAYNAMES.index(day.to_s.capitalize)
end
end
我正在调用控制器的新动作:
class BookingsController < ApplicationController
include BookingsHelper
before_action :set_class_data
skip_before_action :set_page_data,except: :new
def new
set_booking
# store values to be passed to the form helper method options_for_select. Each value must be an array populated with arrays with the format [value,text]
@options = {
class_names: @class_data.map {|c| [ c['name'],c['name'] ]},dates: @booking.available_dates.map {|d| [d.strftime('%A,%d %B'),d.strftime('%A,%d %B')] },times: @booking.available_times.map {|t| [t,t]},attendees: Booking::MAX_ATTENDEES.times.map {|i| [i+1,i+1]}
}
end
def create
end
def booking_form_data
booking_form_data = set_booking_form_data(params)
update_session_booking(booking_form_data)
render json: booking_form_data
end
private
def set_booking
if session[:current_booking]
pp "session exists"
@booking = Booking.new(session[:current_booking])
else
pp "session does not exist"
@booking = Booking.new
session[:current_booking] = @booking.instance_values
end
set_booking_class_data
end
def set_booking_class_data
!@booking.class_name ? @booking.class_data = @class_data.first.except('information') : @booking.class_data = @class_data.find {|cd| cd['name'] == @booking.class_name}.except('information')
end
def booking_params
params.permit(:class_name,:time,:update_type)
end
def update_session_booking(booking_form_data)
if params[:update_type] == 'class_name'
session[:current_booking]['class_name'] = params[:class_name]
session[:current_booking]['date'] = booking_form_data[:date_options].first
session[:current_booking]['time'] = booking_form_data[:time_options].first
elsif params[:update_type] == 'date'
session[:current_booking]['date'] = params[:date]
session[:current_booking]['time'] = booking_form_data[:time_options].first
elsif params[:update_type] == 'time'
session[:current_booking]['time'] = params['time']
elsif params[:update_type] == 'attendees'
session[:current_booking]['attendees'] = params[:attendees]
elsif params[:update_type] == 'load'
session[:current_booking] = booking_params.except(:update_type)
end
pp "Session Booking: #{session[:current_booking]}"
end
def set_booking_form_data(params)
booking_form_data = {}
selected_class = @class_data.find {|cd| cd['name'] == params[:class_name] }
# when the class_name select is changed
if params[:update_type] == 'class_name'
booking_form_data[:date_options] = days_within( selected_class['times'].keys.map {|k| day_index(k) } ).map {|d| d.strftime('%A,%d %B') }
booking_form_data[:time_options] = selected_class['times'][booking_form_data[:date_options].first.split(',')[0].downcase]
# when date select is changed
elsif params[:update_type] == 'date'
booking_form_data[:time_options] = selected_class['times'][params[:date].split(',')[0].downcase]
end
booking_form_data
end
end
还有我用来更新表单的javascript:
getBookingFormData = (bodyData={},successCallback=()=>{}) => {
$.ajax({
url: '/booking_form_data',method: 'POST',beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token',$('Meta[name="csrf-token"]').attr('content'))},data: bodyData,success: successCallback
})
}
createOptions = (values) => {
let newOptions = [];
$.each(values,(index,value) => {
let newOption = $('<option></option>');
newOption.attr('value',value);
newOption.text(value);
newOptions.push(newOption)
})
return newOptions
}
appendOptions = (options,element) => {
$(element).empty();
$(element).append(options)
}
currentFormValues = () => {
return {
class_name: $('#booking_class_name').val(),date: $('#booking_date').val(),time: $('#booking_time').val(),attendees: $('#booking_attendees').val()
}
}
$('select#booking_class_name').on('change',() => {
let bodyData = {
class_name: $('select#booking_class_name').val(),update_type: 'class_name'
}
let successCallback = (res) => {
let dateOptions = createOptions(res.date_options);
let dateSelect = $('select#booking_date');
let timeOptions = createOptions(res.time_options);
let timeSelect = $('select#booking_time');
appendOptions(dateOptions,dateSelect);
appendOptions(timeOptions,timeSelect);
}
getBookingFormData(bodyData,successCallback)
});
$('select#booking_date').on('change',() => {
let bodyData = {
class_name: $('select#booking_class_name').val(),date: $('select#booking_date').val(),update_type: 'date'
};
let successCallback = (res) => {
let timeOptions = createOptions(res.time_options);
let timeSelect = $('select#booking_time');
appendOptions(timeOptions,successCallback)
});
$('select#booking_time').on('change',() => {
let bodyData = {
time: $('select#booking_time').val(),update_type: 'time'
};
getBookingFormData(bodyData);
});
$('select#booking_attendees').on('change',() => {
let bodyData = {
attendees: $('select#booking_attendees').val(),update_type: 'attendees'
};
getBookingFormData(bodyData);
});
$('#create-booking').on('click',(e) => {
e.preventDefault();
bookingDefault = false
const stripe = Stripe(process.env.STRIPE_PUBLIC);
let requestHeaders = new Headers({
'X-CSRF-Token': $('Meta[name="csrf-token"]').attr('content'),'Content-Type': 'application/json'
})
fetch('/create_checkout_session',{
method: 'POST',headers: requestHeaders,body: JSON.stringify(currentFormValues())
})
.then((res) => { return res.json() })
.then((session) => { return stripe.redirecttocheckout({ sessionId: session.id }) })
.then((result) => {
if (result.error) { alert(result.error.message) }
})
.catch((error) => { console.error('Error: ',error) })
})
从我读过的书中,我认为这可能是与缓存有关的问题,这使我认为这是Turbolink的问题,但我可能完全错了。香港专业教育学院试图添加禁用涡轮链接或强制其重新加载页面的元标记,但它们似乎没有用。 我已经坚持了好几天,所以对任何输入都将不胜感激。让我知道您是否需要更多信息
解决方法
这与Stripe无关,而与您的表单价值管理有关。如果要保留这些值,则需要以某种方式将其构建到前端应用程序中。有很多选择:
- 使用本地存储
- 使用查询参数(如果信息不敏感)
- 使用Cookie和服务器会话,您可以使用default value重新获取
f.select
选项并将其混合。