ajax auto form submit with rails4 - javascript

I'm using rails4.2.0
in my e-commerce site, when a user submit his payment type, I want to redirect outside website. The flow is below.
a user choose payment type
use click submit button
-- ajax (format js) --
redirect to outside website using post method
source of 2 and 3 are like ,
create.html.erb
<%= form_for(:user,:url => { controller: "settlements", action: "settlement"}, remote: true, html: {class: :settlement_form}) do |f| %>
<%= f.radio_button :settlement_type, 0 %>paypal
<%= f.radio_button :settlement_type, 1 %>credit card
<%= f.hidden_field :email, :value => #user.email %>
<%= f.hidden_field :fee_type, :value => #user.fee_type %>
<%= f.submit "Submit", data: { disable_with: "Please wait..." }, class: "btn btn-warning" %>
settlement_controller
def settlement
user = User.new(user_params)
if user.save
# parameters for outside website
#payment_params
else
render "new"
end
end
settlement.js.erb
var form = $('<form></form>',{id:"pay",action:'http://outside_ec_site_url/hoge',method:'POST'}).hide();
var body = $('redirect');
body.append(form);
form.append($('<input/>', {type: 'hidden', name: 'something', value: <%= #payment_params[:something] %>}));
form.append($('<input/>', {type: 'hidden', name: 'something', value: <%= #payment_params[:something] %>}))
form.submit();

I fixed this problem, but I want to know better method.
changed settlement.js.erb like,
$("#redirect").html("<%= escape_javascript(render :partial => 'pay' ) %>");
and created new file _pay.html.erb
<script>
$('<form/>', {id: 'paygent',action: "outside_website_url", method: "POST"})
.append($('<input/>', {type: 'hidden', name: 'something1', value: "<%= #payment_params[:somthing1] %>"}))
.append($('<input/>', {type: 'hidden', name: 'something2', value: "<%= #paygent_params[:something2] %>"}))
.appendTo(document.body)
.submit();
</script>
then, it works.
Do you know any other method? Any idea is appriciated.

Related

How to call a javascript function in a rails form?

I have a rails form that looks like this :
= form_for :location, :url=>'/welcome' do |f|
= f.text_field '', placeholder: 'Enter your zip code', id:'input_id'
= f.button "Continue", class: 'button-test'
So when the button continue is clicked upon , rails get the controller and execute the method /welcome
so what i am trying to do is to actually execute a simple javascript function like :
function wawa() {
alert('it works')
};
when the button continue is cliked instead of rails getting to execute the method /welcome.
How can I approach this problem using Javascript only and no library ?
You can use submit() function of jquery.
= form_for :location, :url=>'/welcome', html: {id: "id_form_location"} do |f|
= f.text_field '', placeholder: 'Enter your zip code', id:'input_id'
= f.button "Continue", class: 'button-test'
In javascript file:
$("#id_form_location").submit(function(event) {
alert('it works');
});
You need to add some JavaScript On submit Listener I suppose you need it before form is submit. If you want only on click then have a look at on click Listener also have a look at event prevent as it will help you to prevent from default behaviour of button.
Try this code
<%= form_for :location, :url=>'/welcome' do |f| %>
<%= f.text_field '', placeholder: 'Enter your zip code', id:'input_id' %>
<%= f.button "Continue", class: 'button-test', id: 'demo' %>
<% end %>
<script type="text/javascript">
document.getElementById("demo").addEventListener("click", function(event){
event.preventDefault()
alert('Hello')
});
</script>

Rails data-remote attribute not always requesting a JS response

I have a form_for with remote: :true, but the problem is that it seems to only be submitted remotely about half of the time. Sometimes I will get a ActionController::UnknownFormat (ActionController::UnknownFormat) error from the server, which is configured to only respond to format.js, and most of the time the form will work perfectly.
Is there some way I can call $.rails to make sure that the data-remote binding is taking effect? I have turbolinks turned off. Here is the code for the form:
= form_for [:admin, #seating_chart], remote: 'true', authenticity_token: true, multipart: true do |f|
%br
.form-group.text-center
= f.label :name
= f.text_field :name, class: 'form-control'
.form-group.text-center
= f.label :venue_id
= f.collection_select :venue_id, Venue.all, :id, :name
.form-group.text-center
= f.label :chart_image
= f.file_field :chart_image, id: :seating_chart_file
.actions.text-center
= f.submit 'Save', class: 'btn btn-primary'
\|
= link_to 'Back', :back, class: 'btn btn-primary'
EDIT: upon further investigation it seems to only happen when I upload an image - if I don't upload the image then the error doesn't occur.
Since you are trying to upload file with ajax request it can't be achieved directly.
The solution for this case is remotipart
Remotipart
And this is a demo of it
Demo

Multiple stripe payment buttons on one page of rails 4 app

I'm building a payment page that lists three different subscription options and am using Stripe's checkout to manage the payments.
The page is rendering properly, and all 3 subscription options have the "buy now" button that should be linked to Stripe.
My issue is that the first button is the only one that is properly pulling up the Stripe checkout flow. Buttons 2 and 3 throw the following error:
Unknown action
The action 'index' could not be found for ChargesController
The relevant part of my payment page is:
<% #plans.each do |plan| %>
<li class="col-md-3 plan <%= 'plan-primary' if plan.highlight? %>">
<div class="img-thumbnail">
<div class="caption">
<h3><%= plan.name %></h3>
<h4><%= plan_price(plan) %></h4>
<div class="call-to-action">
<% if #subscription.nil? %>
<% if plan.highlight? %>
<%= form_tag main_app.charges_path do %>
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton" class="btn btn-success">Buy Now</button>
<script>
var handler = StripeCheckout.configure({
key: '<%= 'pk_test_my_pk' %>',
image: '/assets/my_logo.png',
token: function(response) {
var tokenInput = $("<input type=hidden name=stripeToken />").val(response.id);
var emailInput = $("<input type=hidden name=stripeEmail />").val(response.email);
$("form").append(tokenInput).append(emailInput).submit();
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
handler.open({
name: 'My Co',
description: 'Listing subsctiption ($50.00)',
amount: 50*100,
shippingAddress: false
});
e.preventDefault();
});
</script>
<% end %>
<% else %>
<%= form_tag main_app.charges_path do %>
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton" class="btn btn-large btn-primary">Buy Now</button>
<script>
var handler = StripeCheckout.configure({
key: '<%= 'pk_test_my_pk' %>',
image: '/assets/my_logo.png',
token: function(response) {
var tokenInput = $("<input type=hidden name=stripeToken />").val(response.id);
var emailInput = $("<input type=hidden name=stripeEmail />").val(response.email);
$("form").append(tokenInput).append(emailInput).submit();
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options
handler.open({
name: 'My Co',
description: 'Listing subsctiption ($40.00)',
amount: 40*100,
shippingAddress: false
});
e.preventDefault();
});
</script>
<% end %>
<% end %>
Ideas on why only one of the 3 buttons is working properly?
Thanks!
You can get it to seem to work by having unique button ids, e.g.
<button id="<%= dom_id(pricing, 'btn') %>
but there is another problem, with the stripe js. If you execute StripeCheckout.configure multiple times it will create multiple iframes with the same name attribute. Unfortunately this means whatever your customer tries to buy, they will always be sold the last thing you inserted, even if the stripe popup said it was selling them something else.
We used this solution: one form, and dynamically inserting the price and times:
<%= form_tag charges_path, id: 'stripe-payment-form' do %>
<%= hidden_field_tag 'amount', nil, id: 'payment_amount' %>
<%= hidden_field_tag 'name', nil, id: 'payment_name' %>
<%= hidden_field_tag 'days', nil, id: 'payment_days' %>
<% Pricing.all.each do |pricing| %>
<p>
<button id="<%= dom_id(pricing, 'btn') %>">
Buy <%= pricing.name %> for <%= number_to_currency(pricing.pounds, unit: '£') %>
</button>
</p>
<% end %>
<%= javascript_tag do %>
var handler = StripeCheckout.configure({
key: "<%= Rails.configuration.stripe[:publishable_key] %>",
image: "<%= image_path('/images/apple-icons/apple-touch-icon-144x144-precomposed.png') %>",
token: function(token, args) {
var form = $('#stripe-payment-form');
// Use the token to create the charge with a server-side script.
// You can access the token ID with `token.id`
form.append($('<input type="hidden" name="stripeToken" />').val(token.id));
form.submit();
}
});
<% Pricing.all.each do |pricing| %>
document.getElementById('<%= dom_id(pricing, 'btn') %>').addEventListener('click', function(e) {
e.preventDefault();
var form = $('#stripe-payment-form');
// set the price etc for the button clicked
$('#payment_amount').val("<%= pricing.pence %>");
$('#payment_name').val("<%= pricing.name %>");
$('#payment_days').val("<%= pricing.days %>");
// Open Checkout with further options
handler.open({
name: 'Company name',
currency: 'GBP',
description: '<%= pricing.name %>',
amount: '<%= pricing.pence %>',
email: '<%= member.email %>',
});
});
<% end %>
<% end %>
<% end %>
I came across the same problem in my own app recently.
All three of your buttons have the same ID.
I know this is old, but I resolved this issue by changing the name of the handler variables (each one should have a different name) instead of changing the HTML ID's.
I recently encountered this problem and wanted to leave an alternative solution. In our app, we have two buttons on the page using stripe.js: "Buy Item" or "Pro Subscription". This method uses jQuery to just remove the second button from the DOM when the first one is clicked. If the user cancels the payment, the button is rendered back into the DOM. This is how the handler might look:
$('#firstButton').on('click', function() {
$('#secondButton').html(""); // Remove the second stripe script from the dom
handler.open({
// handler stuff
closed: function(){
$('#secondButton').html('<%= j render partial: "second_button" %>'); // Renders button back to the DOM if payment is cancelled.
}
});
});

Completely Stuck on AJAX Form Submission With Stripe

I am so frustrated. I'm working with Stripe to create a form submission system for payments. Basically, the form makes an AJAX call to Stripe, which gives me a token on success, which I then use to resubmit the form, also through AJAX. If the form is successful, it redirects to a new page, if not, it populates the form with error messages without re-navigation. Here's my form:
<%= form_for([#issue, #issue_order]) do |f| %>
<% if #issue_order.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(#issue_order.errors.count, "error") %> occurred. </h2>
<ul>
<% #issue_order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% f.hidden_field :issue_id %>
<%= f.hidden_field :stripe_card_token %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= label_tag :card_number, "Credit Card Number " %>
<%= text_field_tag :card_number, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_code, "Security Code on Card (CVV) " %>
<%= text_field_tag :card_code, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_month, "Card Expiration " %>
<%= select_month nil, {add_month_numbers_true: true}, {name: nil, id: "card_month"} %>
<%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year + 15}, {name: nil, id: "card_year"} %>
</div>
<div id="stripe_error"></div>
<div class="actions"><%= f.submit "Purchase Issue", id: "submit_issue_order" %></div>
<% end %>
<div class="errors"></div>
Here is the javascript that handles the form and sets up the stripe information:
var issueOrder;
$(function() {
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
issueOrder.setupForm();
});
var issueOrder = {
setupForm: function() {
$('#new_issue_order').submit(function(e) {
e.preventDefault();
$('#submit_issue_order').attr('disabled', true);
issueOrder.processCard();
return false;
});
},
processCard: function() {
var card;
card = {
number: $('#card_number').val(),
cvc: $('#card_code').val(),
expMonth: $('#card_month').val(),
expYear: $('#card_year').val()
};
Stripe.createToken(card, issueOrder.handleStripeResponse)
},
handleStripeResponse: function(status, response) {
if (status == 200) {
$('#issue_order_stripe_card_token').val(response.id);
// $('#new_issue_order')[0].submit();
$.ajax({
type: "POST",
url: $('#new_issue_order').attr('action'),
data: { "issue_order": {
"stripe_card_token": $('#issue_order_stripe_card_token').val(),
"email": $('issue_order_email').val(),
},
"issue_id": $('#issue_order_issue_id').val()
},
dataType: "script"
}, issueOrder.processOrder);
}
else {
$('#stripe_error').text(response.error.message);
$('input[type=submit]').attr('disabled', false)
}
}
And here is my controller:
def create
charge = Stripe::Charge.create(
:amount => 400,
:currency => "usd",
:card => params['issue_order']['stripe_card_token']
)
if charge['paid'] == true
#issue_order = IssueOrder.new(email: params['issue_order']['email'], issue_id: params['issue_id'])
if #issue_order.save
#pdf_token = #issue_order.pdf_token
PdfMailer.pdf_email(params['issue_order']['email'], #issue_order).deliver
else·
flash[:error] = []
flash[:error].push("Your card was charged, but sadly we were unable to create·
a record in the database. Please contact us for your copy of the issue.")
respond_to do |format|
format.js
end
end
else
# run checks for errors and return error messages
flash[:error] = []
flash[:error].push("There was an error in processing your payment.")
render :json => {success: false}
end
end
Typical stripe setup stuff. Works fine when the stripe order is successfully processed. Well, it did before I hand-rolled the AJAX call, I'm assuming it'd be fine if I threw a respond_to |format| in the success case in the controller that redirected to the success page. However, for the error cases, my controller renders create.js.erb, which looks like this:
console.log('yo');
$('.errors').empty();
errors = xhr.getResponseHeader('X-Flash-Error').split(',');
<% flash[:error].each do |error| %>
$('.errors').append($('<p>' + <%= error %> + '</p>'));
<% end %>
setTimeout(function() {
$('.errors').empty();
}, 3500);
The controller clearly reaches the file and renders it, as evidenced by the logs:
Started POST "/issues/1/issue_orders" for 127.0.0.1 at 2013-11-12 23:33:17 -0500
Processing by IssueOrdersController#create as JS
Parameters: {"issue_order"=>{"stripe_card_token"=>"tok_102vmu2pSkyWUgPAToj334Oa"}, "issue_id"=>"1"}
(0.4ms) BEGIN
(0.4ms) ROLLBACK
Rendered issue_orders/create.js.erb (0.1ms)
["Your card was charged, but sadly we were unable to create \n a record in the database. Please contact us for your copy of the issue."]
Completed 200 OK in 1363ms (Views: 3.9ms | ActiveRecord: 0.8ms)
But NOTHING happens on my page, including the console log! If I don't hand roll the AJAX and use remote: true, it gets even worse -- it recognizes my submit() call as HTML and doesn't know what to do with the format, rendering an Unknown Format error.
Help!

Complex Cascading Dropdown in Rails won't work in Edit Form

I recently tried to implement a cascading dropdown into my application with this tutorial on petermac.com: http://www.petermac.com/rails-3-jquery-and-multi-select-dependencies/
The tutorial basically talks about doing a cascading dropdown, where every dropdown box is an a separate partial and gets loaded with an jQuery onChange event when the parent select is changed.
Now I got this to work without a problem. But actually my select boxes have quite complicate relationships between them.
So, the form I belongs to a Model called AuditFunction and as the name says is for auditing. Every audit has a source and a target, which can be compared via several commands. The source as well as the target are selected via 3 select boxes. The first box selects the type of database the field is in. The second box selects the table and then the third box selects the field. As the field box can contain thousands of options I tried to implement the cascading dropdown to make it easier for the user to select the field.
To give you an overview, this is what my actions look like:
# new.html.erb
<%= simple_form_for #audit_function do |f| %>
<%= f.input :database_1, :as => :select, :collection => #databases, :include_blank => true %>
<%= render :partial => 'databases_1' %>
<%= render :partial => 'fields_1' %>
<%= f.input :database_2, :as => :select, :collection => #databases, :include_blank => true %>
<%= render :partial => 'databases_2' %>
<%= render :partial => 'fields_2' %>
<% end %>
The javascript for this looks like this:
# jQuery
<script type='text/javascript' charset='utf-8'>
jQuery(function($) {
// when the #country field changes
$("#audit_function_database_1").change(function() {
var database_1 = $('select#audit_function_database_1 :selected').val();
if(database_1 == "") database_1="0";
jQuery.get('/audit_functions/update_database_1_id_select/' + database_1, function(data){
$("#database_1_id").html(data);
})
return false;
});
})
</script>
<script type='text/javascript' charset='utf-8'>
jQuery(function($) {
// when the #country field changes
$("#audit_function_database_2").change(function() {
var database_2 = $('select#audit_function_database_2 :selected').val();
if(database_2 == "") database_2="0";
jQuery.get('/audit_functions/update_database_2_id_select/' + database_2, function(data){
$("#database_2_id").html(data);
})
return false;
});
})
Now I'm only going to show you the partials for database_1_id and field_1_id, but they look the same as database and field 2.
# _databases_1.html.erb
<script type="text/javascript">
jQuery(function($) {
$("#audit_function_database_1_id").change(function() {
var database_1_id = $('select#audit_function_database_1_id :selected').val();
if(database_1_id == "") database_1_id="0";
jQuery.get("/audit_functions/update_field_1_id_select/" + ("<%= params[:id] %>_" + database_1_id), function(data){
$("#field_1_id").html(data);
})
return false;
});
})
</script>
<%= simple_form_for "audit_function" do |f| %>
<% if params[:id] %>
<% if params[:id] == "imp" %>
<%= f.input :database_1_id, collection: AdOriTbl.all.order(ori_filename: :asc).collect{ |a| [a.ori_filename,a.id]} %>
<% elsif params[:id] == "ori" %>
<%= f.input :database_1_id, collection: AdOriTbl.all.order(otb_filename: :asc).collect{ |a| [a.otb_filename,a.id]} %>
<% elsif params[:id] == "mod" %>
<%= f.input :database_1_id, collection: AdQryMod.all.order(qry_mod_text: :asc).collect{ |a| [a.qry_mod_text,a.id]} %>
<% end %>
<% end %>
<% end %>
And now the file containing the target field.
# _fields_1.html.erb
<%= simple_form_for "audit_function" do |f| %>
<% if params[:id] %>
<% if params[:id].gsub(/_{1}\d{1,}\z/, "") == " mod " %>
<%= f.input :field_1_id, collection: AdQryFld.where(ad_qry_mod_id: params[:id].gsub(/\A\w{1,}_{1}/, "").to_i).order(order_id: :asc).collect{ |f| [f.qry_field_text,f.id]} %>
<% else %>
<%= f.input :field_1_id, collection: AdOriFld.where(ad_ori_tbl_id: params[:id].gsub(/\A\w{1,}_{1}/, "").to_i).order(id: :asc).collect{ |f| [f.otb_colhdg,f.id]} %>
<% end %>
<% end %>
<% end %>
The controller then contains all the actions triggered in the javascripts:
# audit_function_conroller.rb
def new
authorize! :new, :audit_functions
#audit_function = AuditFunction.new
#functions = [[I18n.t("text sum"),"sum"],[I18n.t("text quantity"),"quantity"],[I18n.t("text largest_value"),"largest_value"],[I18n.t("text smallest_value"),"smallest_value"]]
#databases = [[I18n.t("text original_database"),"imp"],[I18n.t("text archive_database"),"ori"],[I18n.t("text query_database"),"mod"]]
end
def update_database_1_id_select
if params[:id] == "mod"
type = "mod"
elsif params[:id] == "ori"
type = "ori"
elsif params[:id] == "imp"
type = "imp"
end
render partial: "databases_1", id: type
end
def update_field_1_id_select
type = params[:id]
render partial: "fields_1", id: type
end
Now, as messy as all of this looks, the good thing is that it gets the job done. And to clarify my MVC, these are the relations:
AdOriTbl has_many AdOriFlds
AdOriFld belongs_to AdOriTbl
AdQryMod has_many AdQryFlds
AdQryFld belongs_to AdQryMod
I hope the names don't bother you too much when reading this.
Now lets get back to the problem:
As I said this code works for creating a new object and everything is selected fine. But when I try to edit an object only the field with the database type (database_1 and database_2) are filled. The select boxes for the ID's of the databases are not rendered, while the boxes for the fields are. But all four ID fields are empty.
Now I already tried to fill the boxes by hand with a jQuery that basically looks similar to the ones I already have, but instead of getting triggered onChange, I trigger it when my audit_function has a database_id and render the select box and fill it with the value according value of database_id. This works as well.
The problem is that I can't do this with the field_id, because in the partial of database_1_id where the jQuery for the fields get triggered, I don't have the #audit_function object at hand and also it seems to interfere with the other javascripts.
Besides that I'd also like to think that there is a better way to do this, then my way. But I already tried other tutorials and ways and they either don't work when you don't have your straight-forward Country-State-City relationships or they don't work when editing.
So, any help would be really appreciated. Thanks!
I took the following tutorial as template to rewrite my cascading dropdown:
http://homeonrails.blogspot.de/2012/01/rails-31-linked-dropdown-cascading.html
So, now I throw all the different models into one array and filter it by appending names to the class, to differentiate not only by ID, but also by name. Also the jQuery Plugin chainedTo makes the code much more readable.
So, the controller looks now like this:
#types_for_dropdown = [[I18n.t("text archive_database"),"ori"],[I18n.t("text query_database"),"mod"]]
#tables_for_dropdown = []
#ad_qry_mods = AdQryMod.all
#ad_qry_mods.each do |i|
#tables_for_dropdown = #tables_for_dropdown << [i.qry_mod_text,"mod#{i.id}",{:class => "mod"}]
end
#ad_ori_tbls = AdOriTbl.all
#ad_ori_tbls.each do |i|
#tables_for_dropdown = #tables_for_dropdown << [i.otb_filename,"ori#{i.id}",{:class => "ori"}]
end
#fields_for_dropdown = []
#ad_qry_flds = AdQryFld.all
#ad_qry_flds.each do |i|
#fields_for_dropdown = #fields_for_dropdown << [i.qry_fieldname,i.id,{:class => "mod#{i.ad_qry_mod_id}"}]
end
#ad_ori_flds = AdOriFld.all
#ad_ori_flds.each do |i|
#fields_for_dropdown = #fields_for_dropdown << [i.otb_fieldname,i.id,{:class => "ori#{i.ad_ori_tbl_id}"}]
end
And the form looks like this:
<%= content_for :head do %>
<script>
$(document).ready(function(){
$('select#audit_function_database_1_id').chainedTo('select#audit_function_database_1');
$('select#audit_function_field_1_id').chainedTo('select#audit_function_database_1_id');
$('select#audit_function_database_2_id').chainedTo('select#audit_function_database_2');
$('select#audit_function_field_2_id').chainedTo('select#audit_function_database_2_id');
});
</script>
<% end %>
<div class="grid-6-12">
<%= f.input :database_1, label: I18n.t("field_label audit_function database_1"), hint: I18n.t("field_hint audit_function database_1"), as: :select, collection: #types_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_2, label: I18n.t("field_label audit_function database_2"), hint: I18n.t("field_hint audit_function database_2"), as: :select, collection: #types_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_1_id, label: I18n.t("field_label audit_function database_1_id"), hint: I18n.t("field_hint audit_function database_1_id"), as: :select, collection: #tables_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_2_id, label: I18n.t("field_label audit_function database_2_id"), hint: I18n.t("field_hint audit_function database_2_id"), as: :select, collection: #tables_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :field_1_id, label: I18n.t("field_label audit_function field_1_id"), hint: I18n.t("field_hint audit_function field_1_id"), as: :select, collection: #fields_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :field_2_id, label: I18n.t("field_label audit_function field_2_id"), hint: I18n.t("field_hint audit_function field_2_id"), as: :select, collection: #fields_for_dropdown, include_blank: true %>
</div>
This is really a nice solution and I can recommend it to everyone!

Categories