Please don't flag this as duplicate. I've already reviewed the other similar questions and mine is different.
I have an issue where some of the users that input their addresses manage to create duplicate CartAddress records.
My database structure includes a Cart model and CartAddress model.
The Cart model looks something like this:
class Cart < ApplicationRecord
has_many :cart_addresses, -> { order(address_type: :asc) }, inverse_of: :cart, dependent: :destroy
accepts_nested_attributes_for :cart_addresses, allow_destroy: true
end
My CartAddress model looks like this:
class CartAddress < ApplicationRecord
belongs_to :cart
validates :first_name, presence: true
validates :last_name, presence: true
validates :email, presence: true
validates :telephone, presence: true
validates :address_line_one, presence: true
validates :city, presence: true
validates :country, presence: true
validates :state, presence: true
validates :zip_code, presence: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
When the user loads the cart page:
def index
#bunch of init code here
#more here
#exists = true
if #cart.cart_addresses.count == 0
#exists = false
2.times { #cart.cart_addresses.build(country: "United States", state: "Alabama")}
end
end
Then when I create/update:
def update_addresses
#countries = Country.all
#states = State.all
#cart = Cart.find(params[:cart][:id])
if params[:cart][:cart_addresses_attributes]["0"][:address_type] == "Billing" and params[:cart][:cart_addresses_attributes]["1"][:address_type] == "Shipping"
#cart.update(params.require(:cart).permit(cart_addresses_attributes: [:id, :address_type, :first_name, :last_name, :email, :telephone, :address_line_one, :address_line_two, :city, :zip_code, :country, :state]))
#bunch of other code
end
Here is my partial that renders the form
<%= form_for(cart, :remote => true, :url => update_cart_addresses_path, :html => {:class => 'addresses-class', :id => 'cart-addresses', :autocomplete => :off}) do |u| %>
<% billing = true %>
<% state = 0 %>
<%= u.hidden_field :id %>
<% offset_two = true %>
<% if #exists == true %>
<%= u.fields_for :cart_addresses do |b| %>
<%= b.hidden_field :address_type %>
<% if billing == true %>
<div class="col-xs-12">
<h4>Billing Address</h4>
<% billing = false %>
</div>
<% else %>
<div class="col-xs-12 pad-top-15 checkout-section-padding">
<h4>Shipping Address</h4>
<input type="checkbox" id="same-as-billing" name="same_as_billing" value="1" onclick="toggleShippingAddress(this);">Same as Billing?
</div>
<% end %>
<div class="address">
<div class="col-xs-6">
<%= b.label :first_name %><span class="required-field">*</span>
<%= b.text_field :first_name, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''}%>
</div>
<div class="col-xs-6">
<%= b.label :last_name %><span class="required-field">*</span>
<%= b.text_field :last_name, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :email %><span class="required-field">*</span>
<%= b.email_field :email, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :telephone %><span class="required-field">*</span>
<%= b.text_field :telephone, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :address_line_one %><span class="required-field">*</span>
<%= b.text_field :address_line_one, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.text_field :address_line_two, class: 'checkout-addr-text-field', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :city %><span class="required-field">*</span>
<%= b.text_field :city, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :country %><span class="required-field">*</span>
<%= b.select(:country, countries.all.collect {|a| [a.name]}, {:prompt => "Select A Country"}, :onchange => "setStates(#{state});", class: 'checkout-country') %>
</div>
<div class="col-xs-6 pad-top-15">
<div id="<%= "state_" + state.to_s %>">
<%= b.label :state %><span class="required-field">*</span>
<%= b.text_field :state, class: 'checkout-addr-text-field' %>
</div>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :zip_code%><span class="required-field">*</span>
<%= b.text_field :zip_code, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<% state = state + 1 %>
</div>
<% end %>
<% else %>
<%= u.fields_for :cart_addresses do |b| %>
<% if billing == true %>
<div class="col-xs-12">
<h4>Billing Address</h4>
<% billing = false %>
<%= b.hidden_field :address_type, value: "Billing" %>
</div>
<% else %>
<div class="col-xs-12 pad-top-15 checkout-section-padding">
<h4>Shipping Address</h4>
<input type="checkbox" id="same-as-billing" name="same_as_billing" value="1" onclick="toggleShippingAddress(this);">Same as Billing?
<%= b.hidden_field :address_type, value: "Shipping" %>
</div>
<% end %>
<div class="address">
<div class="col-xs-6">
<%= b.label :first_name %><span class="required-field">*</span>
<%= b.text_field :first_name, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-6">
<%= b.label :last_name %><span class="required-field">*</span>
<%= b.text_field :last_name, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :email %><span class="required-field">*</span>
<%= b.email_field :email, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :telephone %><span class="required-field">*</span>
<%= b.text_field :telephone, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :address_line_one %><span class="required-field">*</span>
<%= b.text_field :address_line_one, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.text_field :address_line_two, class: 'checkout-addr-text-field', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :city %><span class="required-field">*</span>
<%= b.text_field :city, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<div class="col-xs-12 pad-top-15">
<%= b.label :country %><span class="required-field">*</span>
<%= b.select(:country, countries.all.collect {|a| [a.name]}, {:prompt => "Select A Country"}, :onchange => "setStates(#{state});", class: 'checkout-country') %>
</div>
<div class="col-xs-6 pad-top-15">
<div id="<%= "state_" + state.to_s %>">
<%= b.label :state %><span class="required-field">*</span>
<%= b.text_field :state, class: 'checkout-addr-text-field' %>
</div>
</div>
<div class="col-xs-6 pad-top-15">
<%= b.label :zip_code %><span class="required-field">*</span>
<%= b.text_field :zip_code, class: 'checkout-addr-text-field required', :data => {:hj_whitelist => ''} %>
</div>
<% state = state + 1 %>
</div>
<% end %>
<% end %>
<% end %>
<div class="col-xs-12 pad-top-15">
<button id="update-checkout-addresses-btn" class="checkout-addresses-submit-btn btn btn-primary btn-sm" onclick="submitCheckoutAddressesForm(this);" type="button">Update Addresses</button>
</div>
When creating new address records the form is submitted without an id. When editing the records a id is submitted. Somehow it appears that the form is being submitted more than once although I do have javascript disabling the form upon submission. How should I handle this in the controller to prevent this error from happening? Or fix my view partial?
UPDATE
Here is my JS
function submitCheckoutAddressesForm(element){
element.disabled = true;
var e = document.getElementById("same-as-billing")
sameAsBillingCheckout(e);
$(e).closest("form").submit();
}
The below function makes sure that all the elements are the same in the form if a checkbox "Shipping Address Same As Billing" is selected. The reason I am having to clone the element and copy it into the shipping addresses is because the element type has the ability to change due to other javascript. I figured this would be the safest way of handling this event just to ensure that the proper elements were submitted in the form. I don't see that this should effect the duplicate records being created.
function sameAsBillingCheckout(element){
if(element.checked == true){
var billing = ["#cart_cart_addresses_attributes_0_first_name", "#cart_cart_addresses_attributes_0_last_name", "#cart_cart_addresses_attributes_0_email", "#cart_cart_addresses_attributes_0_telephone",
"#cart_cart_addresses_attributes_0_address_line_one", "#cart_cart_addresses_attributes_0_address_line_two", "#cart_cart_addresses_attributes_0_country", "#cart_cart_addresses_attributes_0_state", "#cart_cart_addresses_attributes_0_city", "#cart_cart_addresses_attributes_0_zip_code"]
var shipping = ["#cart_cart_addresses_attributes_1_first_name", "#cart_cart_addresses_attributes_1_last_name", "#cart_cart_addresses_attributes_1_email", "#cart_cart_addresses_attributes_1_telephone",
"#cart_cart_addresses_attributes_1_address_line_one", "#cart_cart_addresses_attributes_1_address_line_two", "#cart_cart_addresses_attributes_1_country", "#cart_cart_addresses_attributes_1_state", "#cart_cart_addresses_attributes_1_city", "#cart_cart_addresses_attributes_1_zip_code"]
for(i = 0; i < billing.length; i++){
$(shipping[i]).val($(billing[i]).val());
if(billing[i] == "#cart_cart_addresses_attributes_0_country"){
$("#cart_cart_addresses_attributes_1_state").remove();
var e = document.getElementById("cart_cart_addresses_attributes_0_state");
var cln = e.cloneNode(true);
cln.name = "cart[cart_addresses_attributes][1][state]";
cln.id = "cart_cart_addresses_attributes_1_state";
var stateDiv = document.getElementById("state_1");
stateDiv.appendChild(cln);
}
}
}
}
This code below does the same as the javascript above. It's just submitting the form after the user finishes typing if the form isn't disabled.
$(function(){
//create one instance for handler:
var myHandler = function(e){
var sAB = document.getElementById("same-as-billing").checked;
var ready = true;
//LOOPS THROUGH ALL THE TEXT FIELDS
$('.checkout-addr-text-field').each(function (){
//IF SAME AS BILLING CHECKBOX SELECTED
if(sAB == true){
//if the field is disabled or the field includes _0_ (means its a billing address field) and the id is not the optional field and the value isn't blank then dont submit the form.
if(this.disabled || this.id.indexOf("_0_") !== -1 && this.id !=
"cart_cart_addresses_attributes_0_address_line_two" && this.value == ""){
ready = false;
}
}else{ // IF SAME AS BILLING CHECKBOX NOT SELECTED
if(this.disabled || this.id !=
"cart_cart_addresses_attributes_0_address_line_two" && this.id !=
"cart_cart_addresses_attributes_1_address_line_two" && this.value == ""){
ready = false;
}
}
});
if(ready == true){
var e = document.getElementById("same-as-billing")
sameAsBillingCheckout(e);
$(e).closest("form").submit();
$("#cart-addresses :input").attr("disabled", true);
}
};
$(document).on('keyup', '.checkout-addr-text-field', debounce(function(e){
myHandler(e);
}, 1700));
$(document).on('change', '#cart_cart_addresses_attributes_0_state', function(e){
myHandler(e);
});
$(document).on('change', '#cart_cart_addresses_attributes_1_state', function(e){
myHandler(e);
});
$(document).on('change', '#cart_cart_addresses_attributes_0_state', function(e){
myHandler(e);
});
$(document).on('click', '#same-as-billing', function(e){
if(this.checked){
myHandler(e);
}
});
});
Related
Here is code sample:
<%= form_for #article, html: { class: "form-horizontal" } do |f| %>
<div class="form-group">
<%= f.label :keywords, class: 'col-md-1 control-label' %>
<div class="col-md-3">
<%= f.select :keywords, ['test_1', 'test_2', 'test_3', 'test_4', 'test_5'], {}, { :multiple => true, :size => 10, :class => 'form-control' } %>
</div>
</div>
<% end %>
When I set multiple to 'false' it works just fine, but if I set it to true (for multiple select), it just doesn't pass any data. If I have verification it gives me an "keyword is empty" error and if I remove validation - it is just empty. Any ideas?
<%= f.select :keywords, options_for_select([['test_1','test_1'], ['test_2','test_2'], ['test_3','test_3'], ['test_4','test_4'], ['test_5','test_5']]), {}, { :multiple => true, :size => 10, :class => 'form-control' } %>
in Main form
<%= p.fields_for :prd_allisland_flat_deliveries do |i| %>
<%= render(:partial => 'prd_allisland_flat_delivery_field', :locals => {:f => i})%>
<% end %>
in the prd_allisland_flat_delivery_field form partial
<div class="row" style="padding-bottom: 25px">
<div class="col-md-2"></div>
<div class="col-md-4">
<%= f.label :delivery_period %>
</div>
<div class="col-md-4">
<%= f.text_field(:delivery_period, {placeholder: '0', class: 'form-control input_border input_field_text_align_right'})%>
</div>
<div class="col-md-2"></div>
</div>
<div class="row" style="padding-bottom: 25px">
<div class="col-md-2"></div>
<div class="col-md-4">
<%= f.label :delivery_rate %>
</div>
<div class="col-md-4">
<%= f.text_field(:delivery_rate, {placeholder: 'Rs. 0.00', class: 'form-control input_border input_field_text_align_right'})%>
</div>
</div>
in prd_item controller
**def new
#item = PrdItem.new
#item.build_prd_allisland_flat_delivery
end**
after writing this the create method
**
if #item.save
if #item.delivery_type == 1
#all_island_flat = PrdAllislandFlatDelivery.new(item_params[:prd_allisland_flat_deliveries_attributes])
#all_island_flat.save
end
end**
the item_params
def item_params
params.require(:prd_item).permit(:item_name, :brand, :item_no, :short_description, :long_description, :prd_type_id, :usr_vendor_property_id, :price,:base_price, :price_discount, :percentage_discount, :stock_count, :availability, :tags, :remove_image, :delivery_type , :min_stock_count,
prd_item_images_attributes: [:id, :image, :description, :link, :_destroy ],
prd_temp_variation_stores_attributes: [:id, :product_variations, :variation_items, :_destroy],
prd_temp_compound_stores_attributes:[:id,:compound, :compound_item, :_destroy],
prd_temp_spec_stores_attributes:[:id,:compound, :compound_item, :_destroy],
prd_allisland_flat_deliveries_attributes: [:id,:delivery_period,:delivery_rate],
prd_province_vise_deliveries_attributes: [:id , :province_name , :delivery_rate, :delivery_period]
)
end
the rails consoler gets the attributes for the prd_allisland_flat_deliveries but the prd_all_island_flat_deliveries fill with null values
Is that your full create method? If so you need to understand that every request is separate from others, the create request doesn't know what happened in the new request.
In your create method you need to build your basic object, then update the object from the params.
def create
#item = PrdItem.new
#item.update(item_params)
if #item.save ...
So i have a search filter working perfectly in my index view. The code in the controller is as follows
def index
#tutor = Tutor.where(:admin => false)
#tutor_array = []
#tutor_array << #tutor.fees_search(params[:fees_search]) if params[:fees_search].present?
#tutor_array << #tutor.subject_search(params[:subject_search]) if params[:subject_search].present?
#tutor_array << #tutor.lssubject_search(params[:lssubject_search]) if params[:lssubject_search].present?
#tutor_array << #tutor.ussubject_search(params[:ussubject_search]) if params[:ussubject_search].present?
#tutor_array << #tutor.jcsubject_search(params[:jcsubject_search]) if params[:jcsubject_search].present?
#tutor_array.each do |tutor|
ids = #tutor.merge(tutor).map(&:id)
#tutor = Tutor.where(id: ids)
end
#tutor = #tutor.sort_by { |tutor| tutor.rating.rating }.reverse
#tutor = #tutor.paginate(:page => params[:page], :per_page => 2)
end
And in my view the form that passes in the search filters for me is
<form class='form-inline'>
<%= form_tag(tutors_path, method: :get) do %>
<div class='row', id='filter-form'>
<div class='form-group'>
<%= label_tag 'subject_search', 'Primary Subject' %>
<% subject_array = Subject.all.map { |subject| [subject.name] } %>
<%= select_tag 'subject_search', options_for_select(subject_array, :selected => params[:subject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'lssubject_search', 'Lower Sec Subject' %>
<% lssubject_array = Lssubject.all.map { |lssubject| [lssubject.name] } %>
<%= select_tag 'lssubject_search', options_for_select(lssubject_array, :selected => params[:lssubject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'ussubject_search', 'Upper Sec Subject' %>
<% ussubject_array = Ussubject.all.map { |ussubject| [ussubject.name] } %>
<%= select_tag 'ussubject_search', options_for_select(ussubject_array, :selected => params[:ussubject_search]), :include_blank => true, class:'form-control' %>
</div>
</div>
<div class='row', id='filter-form2'>
<div class='form-group'>
<%= label_tag 'jcsubject_search', 'JC Subject' %>
<% jcsubject_array = Jcsubject.all.map { |jcsubject| [jcsubject.name] } %>
<%= select_tag 'jcsubject_search', options_for_select(jcsubject_array, :selected => params[:jcsubject_search]), :include_blank => true, class:'form-control' %>
<%= label_tag 'fees_search', 'Max Fees' %>
<%= select_tag 'fees_search', options_for_select((10..150).step(10), :selected => params[:fees_search]), :include_blank => true, class:'form-control' %>
<%= submit_tag 'Filter', class: 'btn btn-primary btn-xs' %>
</div>
</div>
<% end %>
<div id='filter-reset'>
<%= link_to 'Reset Filters', tutors_path, class: 'btn btn-primary btn-xs' %>
</div>
</form>
And when a filter is passed in an example or the URL attained is as follows
/tutors?utf8=✓&subject_search=Science&lssubject_search=&ussubject_search=&jcsubject_search=&fees_search=&commit=Filter
What i would like to ask is, how do i pass in the filters through a button?
So if i were to click on "Math" or "English" its equivalent to passing in the filter through the form? I understand that it'll most likely be link_to tutors_path(???) but what should go into the parenthesis to pass the correct filters in?
I tried <%= link_to subs.name, tutors_path(:subject_search => subs.name %> and it seems like it works. To provide some context here's the view and how subs.name comes about.
<% tutor.subjects.each do |subs| %>
<span class='badge'id='tutor-listing-badge'>
<%= link_to subs.name, tutors_path(:subject_search => subs.name) %>
</span>
<% end %>
I know I must be missing something simple. I have a model that contains a validate_confirmation_of. I know it works in my model (I've tested it using the console) but for some reason, it seems the confirmation field on the form isn't linking into my model correctly and so the model thinks the confirmation field is blank and thus skips the validate.
Model
class Person < ActiveRecord::Base
enum status: { suspended: 0, pending_validation: 1, pending_setup: 2, registered: 3, unknown: 99 }
EMAIL_REGEX = /\A[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}\Z/i
validates :first_name, :presence => true, :length => { :maximum => 255 }
validates :last_name, :presence => true, :length => { :maximum => 255 }
validates :email_address, :presence => true, :length => { :maximum => 255 }, :uniqueness => true, :format => { :with => EMAIL_REGEX }, :confirmation => true
validates :country_code, :presence => true
end
View
<%= form_for #person, url: { action: 'create' } do |f| %>
<fieldset class="registration">
<legend>Your Information</legend>
<div class="group column-1">
<%= label_tag('First Name') %> <span class="required">*</span>
<%= f.text_field :first_name, class: 'form-control', :tabindex => 1 %>
<%= label_tag('Display Name') %>
<%= f.text_field :display_name, class: 'form-control', :tabindex => 3 %>
</div>
<div class="group column-2">
<%= label_tag('Last Name') %><span class="required">*</span>
<%= f.text_field :last_name, class: 'form-control', :tabindex => 2 %>
<%= label_tag('Country') %> <span class="required">*</span>
<%= f.collection_select :country_code, Country.order(:name), :iso_code, :name, { :include_blank => true }, { :class => 'form-control', :tabindex => 4 } %>
</div>
<div class="column-full">
<%= label_tag('Email Address') %> <span class="required">*</span>
<%= f.text_field :email_address, class: 'form-control', :tabindex => 5 %>
<%= label_tag('Confirm Email Address') %> <span class="required">*</span>
<%= f.text_field :email_address_confirmation, class: 'form-control', :tabindex => 6 %>
</div>
<div class="checkbox">
<label>
<%= check_box_tag 'chkTerms' %>
I accept the <%= link_to 'Terms & Conditions', '/pages/terms' %> <span class="required">*</span>
</label>
</div>
<div class="button-options">
<%= f.submit 'Submit', class: 'btn btn-danger' %>
<%= link_to 'Cancel', '/pages/home', class: 'link-button-cancel' %>
</div>
</fieldset>
<% end %>
Controller
def create
#person = Person.new(register_params)
#person.status = 'pending_validation'
if #person.save
render('submit')
else
render('register')
end
end
private
def register_params
params.require(:person).permit(:first_name, :last_name, :email_address, :display_name, :country_code)
end
As you can see, in my view I have a "email_address_confirmation" text_field. I created it the way I created all the other fields and they all work fine so for the life of me I can't figure out what is wrong with the code. But when I submit the page, no matter what I put in the confirmation field, the confirmation validate never fires.
In case it matters, I'm using Rails 4.2.0 and Ruby 2.1.5p273.
You need to add this in your model:
validates :email_address_confirmation, presence: true
According to Rails guides:
This (confirmation) check is performed only if email_confirmation is not nil.
Also make changes in your controller's method register_params, you miss email_address_confirmation field in permit parameters:
def register_params
params.require(:person).permit(:first_name, :last_name, :email_address, :email_address_confirmation, :display_name, :country_code)
end
I'm trying to make the dynamic select just like this http://railscasts.com/episodes/88-dynamic-select-menus-revised?view=asciicast
In my case, it won't work somehow.
What's wrong with my code?
views/registrations/edit.html.erb
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<% resource.build_user_profile if resource.user_profile.nil? %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, :class => 'form-horizontal' }) do |f| %>
<%= devise_error_messages! %>
<%= f.fields_for :user_profile do |profile_form| %>
<%= profile_form.label :country_id %><br />
<%= profile_form.collection_select :country_id, Country.all, :id, :name, include_blank: true %>
<%= profile_form.label :prefecture_id, "State or Prefecture" %><br />
<%= profile_form.grouped_collection_select :prefecture_id, Country.all, :prefectures, :name, :id, :name, include_blank: true %>
<% end %>
<br />
<div class="control-group">
<div class="controls">
<%= f.submit 'Update', :class => 'btn btn-primary' %>
</div>
</div>
<% end %>
user_profiles.js.coffee
jQuery ->
$('#user_profile_prefecture_id').parent().hide()
states = $('#user_profile_prefecture_id').html()
console.log(prefectures)
$('#user_profile_country_id').change ->
country = $('#user_profile_country_id :selected').text()
escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/#])/g, '\\$1')
options = $(prefectures).filter("optgroup[label=#{escaped_country}]").html()
console.log(options)
if options
$('#user_profile_prefecture_id').html(options)
$('#user_profile_prefecture_id').parent().show()
else
$('#user_profile_prefecture_id').empty()
$('#user_profile_prefecture_id').parent().hide()
In any case when you pass:
<%= f.fields_for :user_profile ... %>
field's attributes name= and id= can not be just "#user_profile...", when parent form builder has an object. To determine exactly ID look at the source code (HTML response).