Related
Getting this error when clicking on my Admin page:
"undefined method `user_search_admin_path' for #ActionView::Base:0x0000000010f748" on line 47 url: '<%= user_search_admin_path(:format=>:json) %>'.
I have a route for the "user_search" so not sure what is causing this error. Any ideas how to solve this error?
This is the admin route:
resources :admin, :as => :admin, :only => [:index, :create, :destroy] do
collection {
get "user_search";
get "group_search";
post "toggle_logging";
post "toggle_privs";
get "export_permissions";
get "export_roles";
};
Below are my code files:
index.html.erb
<% content_for :crumbs do %>
<li class="last"><%= link_to("Administrators", admin_index_path) %></li>
<% end %>
<% content_for :javascripts do %>
<script type="text/javascript">
function toggleGroup(group, that){
var el = "." + group;
$(el).parent().toggle(0,"swing",function(){
});
}
function highlightGroup(group, role){
var even = $('tr td.' + group).parent('.even').children("."+role).css("background-color");
var odd = $('tr td.' + group).parent('.odd').children("."+role).css("background-color");
// $('tr td.' + group).parent('.even').children("."+role).animate({backgroundColor:"red", opacity:0.5},800, function(){
// $('tr td.' + group).parent('.even').children("."+role).css({"background-color": even, opacity:1.0});
// });
// $('tr td.' + group).parent('.odd').children("."+role).animate({backgroundColor:"red", opacity:0.5},800, function(){
// $('tr td.' + group).parent('.odd').children("."+role).css({"background-color": odd, opacity:1.0});
// });
$('tr td.' + group).parent('.odd').children('.indented_description').animate({backgroundColor:"red", opacity:0.5},800, function(){
$('tr td.' + group).parent('.odd').children('.indented_description').css({"background-color": odd, opacity:1.0});
});
$('tr td.' + group).parent('.even').children('.indented_description').animate({backgroundColor:"red", opacity:0.5},800, function(){
$('tr td.' + group).parent('.even').children('.indented_description').css({"background-color": even, opacity:1.0});
});
}
$(document).ready(autocomplete_users);
function autocomplete_users() {
$(".add_usernames").autocomplete({
minLength: 3,
source: function(request, response) {
var copy = this.element;
$.ajax({
beforeSend: function(){
$(copy).parent().siblings(".spinner_td").css("display", "block");
},
complete: function() {
$(copy).parent().siblings(".spinner_td").css("display", "none");
},
url: '<%= user_search_admin_path(:format=>:json) %>',
data: {
q: request.term,
},
dataType: "json",
success: function(data) {
response(data);
},
})
},
parse: function(data) {
var parsed = [];
for ( var i = 0; i < data.length; ++i ) {
var row = data[i];
parsed[parsed.length] = {
data: row,
value: row,
result: row
};
}
return parsed;
}
});
}
$(document).ready(autocomplete_groups);
function autocomplete_groups() {
$(".add_groups").autocomplete({
minLength: 3,
source: function(request, response) {
var copy = this.element;
$.ajax({
beforeSend: function(){
$(copy).parent().siblings(".spinner_td").css("display", "block");
},
complete: function() {
$(copy).parent().siblings(".spinner_td").css("display", "none");
},
url: '<%= group_search_admin_path(:format=>:json) %>',
data: {
q: request.term,
},
dataType: "json",
success: function(data) {
response(data);
},
})
},
parse: function(data) {
var parsed = [];
for ( var i = 0; i < data.length; ++i ) {
var row = data[i];
parsed[parsed.length] = {
data: row,
value: row,
result: row
};
}
return parsed;
}
});
}
function add_new_group() {
$.ajax({
url:'auth_role/auth_group_add',
success: function(result) {
//$(result).insertBefore('#add_new_group_button')
$('.group_table').append(result);
autocomplete_groups();
}
});
};
function add_user_to_role() {
$.ajax({
url:'auth_role/auth_user_add',
success: function(result) {
//$(result).insertBefore('#add_new_user_button')
$('.user_table').append(result);
autocomplete_users();
}
});
};
function render_auth_role_partial () {
$.ajax({
url: 'auth_role/' + $("#roles_select").val()+ '/auth_role_partial',
success: function(result) {
$("#edit_role_div").replaceWith(result);
$("#roles_select").value = "<%= #role.id %>"
}
});
};
function show_new_role_form() {
$("#selector_div").hide();
$.ajax({
url: 'auth_role/new',
success: function(result) {
$("#edit_role_div").replaceWith(result);
}
});
};
// so users cannot lock themselves out of managing users
$(document).ready(function() {
$('#perms_checkbox_form').submit(function () {
if ($("input[id^='perm_role_task:manage_user_group']:checked").length == 0) {
alert("There must be at least one role with permission to manage users.");
return false;
}
});
});
function check_duplicate_role() {
var new_name = $("input[id='name']").val();
$.ajax({
url: 'auth_role/check_duplicate_role',
dataType: "json",
contentType: "application/json; charset=utf-8",
data: {new_name: new_name},
success: function(data) {
if (data == true) {
validate_role_form();
} else {
alert("Role already exists or is empty. Please choose another name.");
}
}
});
}
function validate_role_form() {
// Check that the role name is alphanumeric
var role_name = $('#edit_role_div input[name="name"]').val();
if (/[^\w\s]+/.test(role_name)) {
alert("Role name can only contain letters, numbers, _, or whitespace");
$('#edit_role_div input[name="name"]').css("background-color", "#FFB2B2");
return;
}
if ($.trim(role_name).length < 1) {
alert("Role name cannot be blank");
$('#edit_role_div input[name="name"]').css("background-color", "#FFB2B2");
return;
}
var groups = ($("input[id='auth_groups_name']"));
var users = ($("input[id='users_']"));
var group_names = [];
var user_names= [];
var submit_flag = 1;
$.each(groups, (function(index, elem) {
group_names.push($(elem).val());
}))
$.each(users, (function(index, elem){
user_names.push($(elem).val());
}))
if (group_names.length > 0 || user_names.length > 0) {
$.ajax({
url: 'auth_role/check_groups_and_users',
dataType: "json",
contentType: "application/json; charset=utf-8",
data: {group_names: group_names, user_names: user_names},
success:function(data) {
$("input[id='auth_groups_name']").css("background-color", "white");
$("input[id='users_']").css("background-color", "white");
if (data.invalid_groups.length > 0) {
submit_flag = 0;
// mark invalid groups
alert("At least one group is not a valid group.");
$.each(data.invalid_groups, function(index, elem) {
var ind = group_names.indexOf(elem);
$("input[id='auth_groups_name']").eq(ind).css("background-color", "#FFB2B2");
//$("input[id='auth_groups_name'][value='nen_project']").parent().parent().append('<td>Not a valid group.</td>');
});
}
if (data.invalid_users.length >0 ) {
submit_flag = 0;
alert("At least one user is not a valid user.");
$.each(data.invalid_users, function(index, elem) {
var ind = user_names.indexOf(elem);
$("input[id='users_']").eq(ind).css("background-color", "#FFB2B2");
});
}
if (submit_flag == 1) {
$("#update_role_form").submit();
}
}
});
} else {
$("#update_role_form").submit();
}
}
function export_perms() {
window.location.href = "<%= export_permissions_admin_path(:format => 'tsv') %>";
};
function export_roles() {
window.location.href = "<%= export_roles_admin_path(:format => 'tsv') %>";
};
</script>
<% end %>
<% content_for :title do %>
Administrator Panel
<% end %>
<h1><%= yield :title %></h1>
<h2>Build Logging</h2>
<%= form_tag("/admin/toggle_logging", :method =>"post", :id => "toggle_logging") do %>
The build detailed logging is currently <%= "#{#system_settings.detailed_logging ? 'Enabled': 'Disabled'}" %>
<br/><br/>
<%= submit_tag "#{#system_settings.detailed_logging ? 'Disable' : 'Enable'} Logging" ,:class=>"submit_auth_button"%>
<% end %>
<hr>
<% row_class = "even"
groups = []
def replaceSpaces(str)
if str.to_s.strip.length == 0
return str
else
copy = str
copy = copy.gsub " ", "_"
copy = copy.gsub "-", "_"
copy = copy.gsub ":", "_"
copy = copy.gsub "__", "_"
copy = copy.downcase
return copy
end
end
def cleanDescription(str)
if str.to_s.strip.length == 0
return str
else
copy = str
copy = copy.gsub "Edit CR Field: ", ""
copy = copy.gsub "Task: ", ""
copy = copy.gsub "Element: ", ""
return copy
end
end
def cleanDependsOn(str)
if str.to_s.strip.length == 0
return str
else
copy = replaceSpaces(str)
copy = copy.gsub "|", " "
return copy
end
end
%>
<h2>Permissions For Roles </h2>
<%= link_to_function raw("#{image_tag('export.png')} Export Permissions as TSV"), "export_perms()" %>
</br>
</br>
<%= form_tag("/auth_permission/update", :method =>"put", :id => "perms_checkbox_form") do %>
<table>
<tr>
<th class='permissionname'> Permission </th>
<% #roles.each do |role| %>
<th class='rolename'><%=role.name %></th>
<% end %>
</tr>
<% #permissions.each do |perm| %>
<%= fields_for "perm_role[]", perm do |perm_fields| %>
<% if !groups.include? perm.group %>
<% groups << perm.group %>
<tr class='group_header'>
<td onclick='toggleGroup("<%="#{replaceSpaces(perm.group)}"%>", this)'><a href='javascript:void(0);' id='toggler'><%=perm.group%><span id='info_helper'> (click to toggle)</span></a></td>
<% #roles.each do |perm_role| %>
<td class='permission_chkbox all_<%=replaceSpaces(perm.group) %> all_<%=replaceSpaces(perm_role.name) %>'> <%= check_box_tag("group_#{replaceSpaces(perm.group)}_#{replaceSpaces(perm_role.name)}", "unchecked", false ) %>
<% end %>
</tr>
<% end %>
<tr class='<%= row_class %>'>
<td class='indented_description'><%="#{cleanDescription(perm.description)}" %>
<% if perm.depends_desc %>
<br/>
<span class='depends_description <%="#{cleanDependsOn(perm.depends_on)}" %>'>
<%="#{perm.depends_desc}" %>
</span>
<% end %>
</td>
<% #roles.each do |perm_role| %>
<td class='permission_chkbox <%=replaceSpaces(perm.group) %> <%=replaceSpaces(perm_role.name)%>'> <%= check_box_tag("perm_role[#{perm.name},#{perm_role.name}]", "", perm.authroles.include?(perm_role) ) %></td>
<% end %>
<%
if row_class == 'even'
row_class = 'odd'
else
row_class = 'even'
end
%>
<% end %>
</tr>
<%end%>
</table>
<br/>
<%= submit_tag "Update Permissions" ,:class=>"submit_auth_button"%>
<% end %>
<script>
<% usednames = [] %>
<% jsgroups = [] %>
<% #permissions.each do |perm| %>
<% #roles.each do |perm_role| %>
var elementId = '#<%="group_#{replaceSpaces(perm.group)}_#{replaceSpaces(perm_role.name)}"%>';
<% if !jsgroups.include? "#{perm_role.name} #{perm.group}" %>
<% jsgroups << "#{perm_role.name} #{perm.group}" %>
$(elementId).click(function(){
// Check all the children
if(this.checked){
$(elementId).prop("checked", true);
//console.log($('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input'))
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').each(function() {
$(this).prop("checked", true)
});
}
else{
$(elementId).prop("checked", false);
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').each(function() {
$(this).prop("checked", false)
});
}
});
<% end %>
// Need to clear out this variable otherwise it'll get confused
elementId = ""
// Check if everything under that group/role is already checked
// If it is then check the Group Header
// If it's not don't do anything
var is_all_checked = true;
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').each(function(){
if(!this.checked || typeof(this.checked) == "undefined") is_all_checked = false
});
if(is_all_checked){
$('#<%="group_#{replaceSpaces(perm.group)}_#{replaceSpaces(perm_role.name)}"%>').prop("checked", true);
}
// If any of the children permission roles get change, let's figure out if we need to check/uncheck
// the parent
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').change(function(){
// Check if everythin in that gorup is already filled out
var is_all_checked = true;
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').each(function(){
if(!this.checked || typeof(this.checked) == "undefined") is_all_checked = false
});
if(is_all_checked){
$('#<%="group_#{replaceSpaces(perm.group)}_#{replaceSpaces(perm_role.name)}"%>').prop("checked", true);
}
if(this.checked){
}
else{
// uncheck the parent if any of its children are unchecked
$('#<%="group_#{replaceSpaces(perm.group)}_#{replaceSpaces(perm_role.name)}"%>').prop("checked", false);
//console.log("uncheck parent");
}
});
<% t = ".#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)} input" %>
<% if !usednames.include? t %>
<% usednames << t %>
$('.<%="#{replaceSpaces(perm.group)}.#{replaceSpaces(perm_role.name)}"%> input').change(function(){
var rolename = '<%="#{replaceSpaces(perm_role.name)}"%>';
var that = this;
var old_color = $(that).parent().parent().children().first().children('.depends_description').css('background-color')
$(that).parent().parent().children().first().children('.depends_description').animate({backgroundColor:'red', opacity:0.75}, 500);
$(that).parent().parent().children().first().children('.depends_description').animate({backgroundColor:old_color, opacity:1}, 100);
var classes = $(that).parent().parent().children().first().children('.depends_description').attr("class");
if(classes && classes != "") {
classes = classes.split(" ")
for(var k in classes){
if(classes[k] != "depends_description"){
// console.log("toggle", classes[k])
highlightGroup(classes[k], rolename);
}
}
}
});
<% end %>
<% end %>
<% end %>
</script>
<hr/>
<h2> Roles </h2>
<%= link_to_function raw("#{image_tag('export.png')} Export Roles as TSV"), "export_roles()" %>
</br>
</br>
<% if #role %>
<div id="selector_div">
<table>
<tr>
<td> Select Role: </td>
<td>
<%= select_tag "roles_select", options_for_select(#roles.collect {|role_option| [role_option.name, role_option.id]}, :selected => #role.id), :onchange => "render_auth_role_partial()", :class=>"role_selection" %>
</td>
<td>
<%= button_to_function "Create New Role", "show_new_role_form()", :class=>"small_auth_button" %>
</td>
<td>
</td>
</tr>
</table>
</div>
<% end %>
<hr>
<%= render :partial => 'auth_role_form', :locals => {:role => #role, :roles => #roles}%>
admin_controller.rb
class AdminController < ApplicationController
before_action :check_permission
def index
puts "stepped in index"
#system_settings = AuthSettings.first
#permissions = AuthPermission.sort(:group.asc, :name.asc)
#roles = AuthRole.all(:order => :name)
#role = #roles.first
#users = User.administrators
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users, :status => :ok }
format.json { render :json => #users, :status => :ok }
end
end
def create
usernames = params[:admin][:usernames].split(',')
#users = []
usernames.each do |username|
username.strip!
next if username.empty?
begin
#users << User.find_or_create_admin(username)
rescue
logger.error("Couldn't save user with name: #{username}")
end
end
respond_to do |format|
format.html { redirect_to(:action=>:index) }
format.xml { render :xml => #users, :status=>:created }
format.json { render :json => #users, :status=>:created }
format.js { render :layout=>false }
end
end
def destroy
#user = User.find_by_name(params[:id])
if #user
#user.admin = false
if #user.save
respond_to do |format|
format.html
format.xml { render :xml => #user }
format.js { render :layout => false }
end
end
else
redirect404
end
end
def user_search
username = params.delete(:q)
if username
#users = User.search_for_usernames(username)
respond_to do |format|
format.json { render :json => #users }
end
else
# TODO: this is erroneous because this particular request is always going to be a JSON request via the view
# If the request was made from the outside, and it was an HTML request, it's not going to render anything...
# regardless of whether or not the q parameter was supplied
flash[:error] = "You need to supply a 'q' parameter in order to search"
respond_to do |format|
format.html { render :action=>:index }
end
end
end
def group_search
groupname = params.delete(:q)
if groupname
#groupnames = User.search_for_groups(groupname)
respond_to do |format|
format.json { render :json => #groupnames }
end
else
# TODO: this is erroneous because this particular request is always going to be a JSON request via the view
# If the request was made from the outside, and it was an HTML request, it's not going to render anything...
# regardless of whether or not the q parameter was supplied
flash[:error] = "You need to supply a 'q' parameter in order to search"
respond_to do |format|
format.html { render :action=>:index }
end
end
end
def toggle_logging
Rails.logger.info params
setting = AuthSettings.first
setting.detailed_logging = !setting.detailed_logging
Rails.logger.info "Logging is #{setting.detailed_logging}"
if setting.save
flash[:notice] = "Detailed logging #{setting.detailed_logging ? 'enabled' : 'disabled'}."
else
flash[:error] = "Could not change logging"
end
redirect_to admin_index_path
end
def toggle_privs
current_user.admin_enabled = !current_user.admin_enabled
if current_user.save
flash[:notice] = "Administrative privileges #{current_user.admin_enabled ? 'enabled' : 'disabled'}."
else
flash[:error] = "Could not change your privilege level."
end
redirect_to admin_index_path
end
def export_permissions
respond_to do |format|
format.tsv do
tsv = []
column_headers = [:description, :depends_desc, :authroles]
# Filter records.
records = AuthPermission.sort(:group.asc, :name.asc)
# Render TSV lines.
records.each do |record|
element_line = []
column_headers.each do |column|
begin
if column == :authroles
value_arr = []
value = ""
record.authroles.each do |role|
value_arr << role.name
end unless !record.authroles
value << value_arr.join(",")
else
value = eval("record.#{column}")
end
element_line << value
rescue
element_line << nil
end
end
tsv << element_line.join("\t")
end
# Add column headers to the top of the matrix.
tsv.unshift([column_headers].flatten.join("\t"))
render :text => tsv.join("\r\n")
end
end
end
def export_roles
respond_to do |format|
format.tsv do
tsv = []
column_headers = [:name, :description, :groups, :users]
# Filter records.
records = AuthRole.all(:order => :name)
# Render TSV lines.
records.each do |record|
element_line = []
column_headers.each do |column|
begin
if column == :groups
value_arr = []
value = ""
record.groups.each do |group|
value_arr << group.name
end unless !record.groups
value << value_arr.join(",")
elsif column == :users
value = record.users.join(",")
else
value = eval("record.#{column}")
end
element_line << value
rescue
element_line << nil
end
end
tsv << element_line.join("\t")
end
# Add column headers to the top of the matrix.
tsv.unshift([column_headers].flatten.join("\t"))
render :text => tsv.join("\r\n")
end
end
end
private
def check_permission
ensure_task_permission('task:manage_user_group')
end
end
As per your routes definition, if the URI needed for a GET request is is /admin/user_search then you should be using user_search_admin_index_path - notice the _index suffix.
I am trying to build a form that allows a user to make a booking for a martial arts class they wish to attend. I have created a form that dynamically changes based on the selections the user makes, when I change any of the select options the form updates and when I submit the form it redirects to a Stripe checkout. The problem I have is after submitting the the form and I click the browsers back button or the back button provided on the Stripe checkout page the select options that have been updated have reverted back to the default options rather than the updated options. Can anyone help me correct this behaviour and get the correct form elements to persist?
Here is the code I am using to do this:
The view I am rendering the form in:
<% 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>
The form I am using:
<%= 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>
The model for the form (I'm not using ActiveRecord, i dont know if this makes any difference?):
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
And the controller I am calling the new action in:
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, :date, :time, :attendees, :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
And the javascript I am using to update the form:
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, timeSelect);
}
getBookingFormData(bodyData, 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) })
})
From what Ive read i think it may be a problem related to caching which makes me think this is an issue with turbolinks but I could be completely wrong. Ive tried adding meta tags that disable turbolinks or force it to reload the page but they did not seem to work.
Any input at all would be really appreciated as Ive been stuck on this for days. Let me know if you need any more information
This isn't so much Stripe-related as it is related you your form value management. If you want to keep these values around, you'll need to build that into your front application, somehow. There are lots of options for this:
Using local storage
Using query parameters, if not sensitive info
Using a cookie and a server session you can re-retrieve and hydrate the f.select options with a default value.
I'm new at RoR and I'm having a trouble in my app. The problem consists on filter a select field named "Solution", based on the others select fields above it.
Now, what the app do is to retrieve all information from BD about Area, Region, Associated, Solution and populate the select fields with these data. But the user wants that, when an area, a region and an associated is selected by the user, only the solutions about that associated in that region on that area should be shown.
Edit:
I'm almost there! I've made many changes in my app. The select fields are populated by controller action new and the function "populate_selects", which is called by the parameter before_action :popula_selects, only: [:new, :edit]. A new function was created in order to be called by AJAX and upgrade the "Solution" field:
Atendments_Controller < ApplicationController
before_action :populate_selects, only: [:new, :edit]
def new
#atend = atendment.new
end
def update_solution #AJAX
#solutions = atendment.joins(:solution).where("atendment_area_id = ? and atendment_region_id = ? and atendment_assoc_id = ?", params[:atendment_area_id], params[:atendment_region_id], params[:atendment_assoc_id])
respond_to do |format|
format.js
end
end
private
def populate_selects
#atendment_area = atendmentArea.where(status: true, user_id: current_user.id)
#atendment_region = atendmentRegion.where(status: true, user_id: current_user.id)
#atendment_assoc = atendmentRegionAssoc.where(status: true, assoc_id: current_user.entidade_id).where(atendment_region_id: #atendment_region.map(&:atendment_region_id))
#solutions = atendment.joins(:solution).where("atendment_area_id = ? and atendment_region_id = ? and atendment_assoc_id = ?", params[:atendment_area_id], params[:atendment_region_id], params[:atendment_region_assoc_id])
end
end
Below, the _form.html.erb code from view:
<div class="atendment-form">
<%= form_for :atendment, url: {action: "new"}, html: {method: "get"} do |f| %>
<div class="col-xs-6">
<%= f.select :atendment_area_id, options_for_select(#atendment_area.collect { |c| [ c.atendment_area.name, c.id ] }, 1), {:prompt=>"Área"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_area_id' } %>
</div>
<div class="col-xs-6">
<%= f.select :atendment_region_id, options_for_select(#atendment_region.collect { |c| [ c.atendment_region.name, c.id ] }, 1), {:prompt=>"Região"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_region_id' } %>
</div>
</div>
</div>
<div class="field">
<%= f.select :atendment_assoc_id, options_for_select(#atendment_assoc.collect { |c| [ c.atendment_region.name, c.id ] }, 1), {:prompt=>"Associado"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_assoc_id' } %>
</div>
<div class="field">
<%= f.select :solution_id, options_for_select(#solutions.collect { |solution| [solution.name, solution.id] }, 0), {:prompt=>"Solução"}, { :class => 'form-control', :required => true, id: 'atendment_solution_id' } %>
</div>
</div>
Route to the new function:
resources :atendments do
collection do
get :update_solution
end
end
AJAX function which calls the "update_solution" and reset solution field's value (app/assets/javascript/atendment.js.coffee):
show_solutions = ->
$.ajax 'update_solution',
type: 'GET'
dataType: 'script'
data: {
atendment_area_id: $("#atendment_atendment_area_id").val()
atendment_region_id: $("#atendment_atendment_region_id").val()
atendment_assoc_id: $("#atendment_atendment_assoc_id").val()
}
error: (jqXHR, textStatus, errorThrown) ->
console.log("AJAX Error: #{textStatus}")
success: (data, textStatus, jqXHR) ->
console.log("OK!")
$(document).ready ->
$('#atendment_atendment_assoc_id').on 'change', ->
show_solutions()
So, I've created a .coffee file to render the partial that will return a new value to the "solution" field "option" tag
(app/views/atendment/update_solution.coffee):
$("#atendment_solution_id").empty()
.append("<%= escape_javascript(render :partial => 'solution') %>")
And, the last but not least, the partial containing the html code for the "option" tag mentioned above (app/views/atendments/_solution.html.erb):
<option value="<%= solution.id %>" selected="selected"><%= solution.nome %></option>
For any reason, the AJAX function doesn't print nothing on console (nor error neither success), but it calls the update_solution.coffee file. The point is, it doesn't update the option value due an error (500 internal server error). I don't know what am I doing wrong. If anybody could help me, I appreciate it.
I would do this with JS, can think any other way.
A function called by onchange that change the display attribute from each field that you need to hide or show.
I solved this with the following code:
assets/js/atendments.js
I changed the code because the last one had many bugs.
function getAssociated(){
var aau_id = $("#atendment_area_user_id").val()
var aru_id = $("#atendment_region_user_id").val();
$.getJSON("/controllers/atendments_controller/getAssociated/"+aru_id,
function ( callback ) {
if (callback != "error"){
var assoc = document.getElementById("atendment_region_associated_id");
while (assoc.firstChild) {
assoc.removeChild(assoc.firstChild);
}
var i = Object.keys(callback).length -1;
$("#atendment_region_associated_id").append("<option value=''>Associated</option>");
while (i >= 0) {
$("#atendment_region_associated_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i--;
}
}
});
get_solution_type();
}
function get_solution_type() {
var ara_id = $("#atendment_region_associated_id").val();
$.getJSON("/controllers/atendments_controller/getSolution/"+ara_id,
function ( callback ) {
if (callback != "error"){
var sol = document.getElementById("atendment_solution_id");
while (sol.firstChild) {
sol.removeChild(sol.firstChild);
}
var i = Object.keys(callback).length-1;
while (i >= 0) {
$("#atendment_solution_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i--;
}
}
});
var aau_id = $("#atendment_area_user_id").val();
$.getJSON("/controllers/atendments_controller/getType/"+aau_id,
function ( callback ) {
if (callback != "erro"){
var type = document.getElementById("atendment_type_id");
while (type.firstChild) {
type.removeChild(type.firstChild);
}
var i = 0;
while (i < (Object.keys(callback).length)) {
$("#atendment_type_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i++;
}
}
});
}
The $.getJSON performs ajax request to the controller that responds with JSON and update the select fields option tags.
controllers/atendments_controller
I just retrieve the data from DB and return as JSON
def getAssociated
aru_id = params[:atendment_region_user_id]
aras = AtendmentRegionAssociated.where("SQL here")
if aras.present?
render :json => aras.to_json
else
render :json => "error".to_json
end
end
def getSolution
ara_id = params[:atendment_region_associated_id]
sol = Solution.where("SQL here")
if sol.present?
render :json => sol.to_json
else
render :json => "error".to_json
end
end
def getType
aau_id = params[:atendment_area_user_id]
type = AtendmentType.where("SQL here")
if type.present?
render :json => type.to_json
else
render :json => "error".to_json
end
end
Update the routes and put the javascript functions in select fields onchange property. Now everything is working fine :D
I have a controller with this code
def index
if params["destination"].present?
#destination = params["destination"]
#destination_name = params["destination_name"]
#new_loc = (params["current_destination"].present? && (params["destination"] != params["current_destination"]))
......
......
if (#new_loc == false) && params["city_areas"].present?
city_area_ids = params["city_areas"].map(&:to_i)
#hotels = #hotels.select{|ht| city_area_ids.include?( #booking_hotel_partner_details.select{|f| f.partner_booking_hotel_id==ht['id'].to_i}.first.booking_hotel.hotel_city_area_id ) }
end
end
hotels.index.html.haml
$('.checkbox-custom').on('change',function(){
filter_home_stays();
$('#searchForm').trigger('submit.rails');
});
hotels.index.js.erb
if (new_loc == "true") {
$('#city-areas').children('ul.open-dropdown').remove();
$('#city-areas').append('<%= escape_javascript(render :partial => 'filter', :locals => ({:city_areas => #city_areas})) %>');
$('#city_areas .local-amenities-drpdown').mCustomScrollbar({
theme:"rounded-dark",
mouseWheelPixels: 500,
});
}
when click checkbox first time the filter works properly.
But after I change the destination it doesn't work
Can someone help me please?
This is what I did.
if (new_loc == "true") {
$('#city-areas').children('ul.open-dropdown').remove();
$('#city-areas').append('<%= escape_javascript(render :partial => 'filter', :locals => ({:city_areas => #city_areas})) %>');
$('#city_areas .local-amenities-drpdown').mCustomScrollbar({
theme:"rounded-dark",
mouseWheelPixels: 500,
});
$('.city_area.checkbox-custom').on('change',function(){
filter_home_stays();
$('#searchForm').trigger('submit.rails');
});
}
Thanks guys for giving it a shout.You guys rock!
I'm trying to implementing an endless scroll on my project. I'm using a mix of the Railscast #114 Endless Page and this.
Everything works fine besides a weird behavior when I try to stop sending requests when the page hits its end.
So far I have:
Controller:
def show
#title = Photoset.find(params[:id]).name
#photos = Photoset.find(params[:id]).photo.paginate(:page => params[:page], :per_page => 20)
respond_to do |format|
format.js
format.html
end
end
Show.html.erb:
<% content_for :body_class, '' %>
<%= render 'shared/header' %>
<div id="photos_container">
<div id="photos_header">
<h2><%= #title %></h2>
</div>
<%= render :partial => 'photo', :collection => #photos %>
</div>
<%= render :partial => 'endless_scroll' %>
Javascript (loaded via partial):
<script type="text/javascript">
(function() {
var page = 1,
loading = false,
finish = false;
function nearBottomOfPage() {
return $(window).scrollTop() > $(document).height() - $(window).height() - 200;
}
function finish() {
finish = true;
}
$(window).scroll(function(){
if (loading) {
return;
}
if(nearBottomOfPage() && !finish) {
loading=true;
page++;
$.ajax({
url: '/photosets/<%= params[:id] %>?page=' + page,
type: 'get',
dataType: 'script',
success: function() {
loading=false;
}
});
}
});
}());
</script>
show.js.erb
$("#photos_container").append("<%= escape_javascript(render :partial => 'photo', :collection => #photos) %>");
<% if #photos.total_pages == params[:page].to_i() %>
page.call 'finish'
<% end %>
As you can see, on my show.js.erb I have a page.call that assigns true to the finish variable. This stops the requests.
The wired thing is that it never loads the last page. When #photos.total_pages == params[:page].to_i() instead of just calling the finish function and setting the variable to true, it's also preventing the $("#photos_container").append("<%= escape_javascript(render :partial => 'photo', :collection => #photos) %>"); from running.
It sends the request to the controller, runs the SQL but doesn't append the last page.
If I change the condition to #photos.total_pages < params[:page].to_i() it works, but send an extra request to a page that doesn't exist.
I'd appreciate any help on my implementation. I'm not sure if there's a more adequate (Rails) way to accomplish this.
First of all you can render html from the partial when request is xhr type:
def show
photoset = Photoset.includes(:photos).find(params[:id])
#title = photoset.name
#photos = photoset.photo.paginate(:page => params[:page], :per_page => 20)
if request.xhr?
render '_photo', :layout => false
end
end
Then use ajax call:
$.ajax({
url: '/photosets/<%= params[:id] %>?page=' + page,
type: 'get',
dataType: 'script',
success: function(response) {
$("#photos_container").append(response);
if (response == "") {
//stop calling endless scroll
}
});
});