Why does my stripe elements form not work on rails? - ruby-on-rails

I'm trying to get Stripe.js to work on my rails app. I am using stripe elements. I have followed the instructions on the stripe docs. The form card input is not showing up where it should. It just shows the submit button.
I am trying to place the Stripe form in an existing rails form I have.
<!DOCTYPE html>
<html>
<head>
<title>Bennett</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script src="https://js.stripe.com/v3/"></script>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render '/home/nav' %>
<%= yield %>
</br>
<%= render '/home/footer' %>
</body>
</html>
VISITS/_FORM
<%= simple_form_for([#service, #visit]) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<%= f.input :visit_date, as: :date , :label => "Service date:"%>
<%= f.input :visit_time, as: :time , :label => "Service time:" %>
<%= f.input :note, :label => "Anything we should know?" %>
<%= render 'charges/payment-form' %>
<%= f.button :submit, class:'btn btn-bennett' %>
<% end %>
_PAYMENT-FORM
<form action="/charge" method="post" id="payment-form">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
CHARGE.JS
// Create a Stripe client.
var stripe = Stripe('XXXXXXXXXXXXXX');
// Create an instance of Elements.
var elements = stripe.elements({
fonts: [
{
cssSrc: 'https://fonts.googleapis.com/css?family=Roboto',
},
],
// Stripe's examples are localized to specific languages, but if
// you wish to have Elements automatically detect your user's locale,
// use `locale: 'auto'` instead.
locale: window.__exampleLocale
});
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
}
});
});
// Submit the form with the token ID.
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
I don't know why the form is not showing up.
Basically what I am aiming to get accomplished is have it so when I create a new 'Visit' through the Visit form, I also process a payment through stripe and it all gets submitted through one button. Maybe someone can help me with that also?

I had the same problem, solved it by adding this to my css:
.StripeElement{
min-width: 300px;
}

It looks like you aren't waiting for the page to load to execute your script, hence the error that it can't find #card-element.
You should wrap your code to wait for the page load:
document.addEventListener('turbolinks:load',function () {
....Your code
}

Related

How to include an url in application.html.erb in rails 6?

I am trying to add Google Map in my rails app. I followed the guidance link: https://medium.com/#pjbelo/using-google-maps-api-v3-with-rails-5-2-b066a4b2cf14
(But I use rails 6)
and I have my code:
application.html.erb:
<head>
<title>GmapTry</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'https://maps.googleapis.com/maps/api/js?key=AIzaSyA7ClwnGU9QTuNhY3DuVqM5K5YbWA7zJsI' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= yield(:head_tags) %>
</head>
<body>
<%= yield %>
</body>
<% provide :head_tags do %>
<meta name='turbolinks-visit-control' content='reload'>
<script>
document.addEventListener("DOMContentLoaded", function(){
initMap(<%=#place.latitude%>, <%=#place.longitude%>)
});
</script>
<% end %>
...
...
<p>
<div id="map"></div>
</p>
places.js
function initMap(lat, lng) {
var myCoords = new google.maps.LatLng(lat, lng);
var mapOptions = {
center: myCoords,
zoom: 14
};
var map = new google.maps.Map(document.getElementById('map'), mapOptions);
}
application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("gmaps/google")
require("packs/places")
Actually when I followed the first step to show the static map, it works well like:
but when I go to next step with js, nothing shows up now. Anybody knows why?
In rails 6, is there anything wrong to write %= javascript_include_tag 'https://maps.googleapis.com/maps/api/js?key=AIzaSyA7ClwnGU9QTuNhY3DuVqM5K5YbWA7zJsI' %in application.html.erb?
Every maps tutorial I have seen leads to a dead end of inline script tags and globals when its actually really pretty easy to do the job "the right way".
Just use an element with data attributes to pass information from the view to your JavaScript:
<%= content_tag :div,
"",
class: 'google-maps-widget', # call it whatever you want
data: {
lat: #place.latitude,
lng: #place.longitude
}
%>
You can ideally DRY this code into a helper method. Then just setup an event handler that loads the map on the initial page load or when turbolinks refreshes the page.
document.addEventListener("turbolinks:load", function(){
// find the maps on the page
let maps = document.querySelectorAll(".google-maps-widget");
// loop through the maps
maps.forEach(function(element){
let data = element.dataset;
if (!data.initialized) {
let map = new google.maps.Map(element, {
// you can just use a object literal instead
center: { lat: data.lat, lng: data.lng },
// lets you override the inital zoom
zoom: data.zoom || 14
});
// This keeps the element from being re-initialized
// if turbolinks persists it
element.dataset.initialized = "true";
}
});
});
If you want to add a bunch of markers to the map you can either just add a bunch of elements inside the <div class="google-maps-widget"... element:
<div class="google-maps-widget" ... >
<div class="marker hidden" data-lng="1" data-lng="2">My Favorite spot</div>
...
<end>
You would then parse these out when initializing the map. Or you add a data attribute to the map element with a URI where you can fetch the markers via AJAX.

Google Map Javascript Only loads after refresh

Using Google Map API, I have embedded Google Map (Javascript) in my page. However, the map will only load after I refresh the page once.
I looked up on many past threads, to which many mentioned the caused related turbolinks.
I tried many of those but still it didn't work.
Application.html.erb
<%= yield %>
<%= javascript_include_tag "https://maps.googleapis.com/maps/api/js?libraries=places&key=#{ENV['GOOGLE_API_BROWSER_KEY']}" %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application' %>
<%= javascript_pack_tag "map" %>
Show.html.erb
<div id="map"
style="width: 100%;
height: 300px;"
data-markers="<%= #markers.to_json %>"
></div>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_API_BROWSER_KEY'] %>&callback=initMap"
type="text/javascript"></script>
javascript/packs/map.js
import GMaps from 'gmaps/gmaps.js';
const mapElement = document.getElementById('map');
if (mapElement) {
const map = new GMaps({ el: '#map', lat: 0, lng: 0 });
const markers = JSON.parse(mapElement.dataset.markers);
map.addMarkers(markers);
if (markers.length === 0) {
map.setZoom(2);
} else if (markers.length === 1) {
map.setCenter(markers[0].lat, markers[0].lng);
map.setZoom(14);
} else {
map.fitLatLngBounds(markers);
}
}
Please let me know if you have any additional question or if you need anymore info.
Appreciate all your helps!

Stripe element form is not visible in a rails app

I am using Stripe elements and the form is not displaying in my rails app.
I have tried many suggestions and still no improvement. I am
using ruby 2.3 & Rails 5.2.3 but not bootstrap.
charges.js
document.addEventListener("turbolinks:load", function() {
const public_key = document.querySelector("meta[name='stripe-public-
key']").content;
const stripe = Stripe(public_key);
const elements = stripe.elements();
const style = {
base: {
color: '#32325d',
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
const card = elements.create('card', {style: style});
card.mount('#card-element');
card.addEventListener('change', ({error}) => {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = '';
}
});
const form = document.getElementById('new_job');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const {token, error} = await stripe.createToken(card);
if (error) {
// Inform the customer that there was an error.
const errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
// Send the token to your server.
stripeTokenHandler(token);
}
});
const stripeTokenHandler = (token) => {
// Insert the token ID into the form so it gets submitted to the server
const form = document.getElementById('new_job');
const hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
["brand", "exp_month", "exp_year", "last4"].forEach(function(field) {
addFieldToForm(form, token, field);
});
// Submit the form
form.submit();
}
function addFieldToForm(form, token, field) {
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', "user[card_" + field + "]");
hiddenInput.setAttribute('value', token.card[field]);
form.appendChild(hiddenInput);
}
});
application.html.erb
<!DOCTYPE html>
<html>
<head>
<title></title>
<%= csrf_meta_tags %>
<meta name="viewport" content="width=device-width, initial-scale=1">
<%= stylesheet_link_tag 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css' %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', "https://js.stripe.com/v2/", "https://js.stripe.com/v3/", 'data-turbolinks-track': 'reload' %>
<%= tag :meta, name: "stripe-public-key", content: Figaro.env.stripe_publishable_key %>
</head>
<body>
</body
</html>
payments form
<div class="form-row">
<label for="card-element" class="label">
Enter credit or debit card
</label>
<div id="card-element" class="form-control">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- used to display Element errors -->
<div id="card-errors" role="alert"></div>
</div>
For using stripe elements, you need to run your application with https, read more in stripe documentations https://stripe.com/docs/stripe-js/elements/quickstart#http-requirements

Google recaptcha disappears after second click in the signup button on navbar

In my website recaptcha disappears after second click in sign up button or home button in top navbar. User can signup without verification. How to solve it.
<h2>Create Blog</h2>
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), class: "form-horizontal" ) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :username, required: true, autofocus: true, hint: 'no special characters, no numbers only letters [a-z]' %>
<%= f.input :password, required: true, hint: ("#{#minimum_password_length} characters minimum" if #minimum_password_length) %>
<%= f.input :password_confirmation, required: true %>
<div id="g-recaptcha" class="g-recaptcha" data-sitekey="6LcOGEgUAAAAAOxXmBxxxxxxxx"></div>
</div>
<div class="form-actions">
<%= f.button :submit, "Create Blog", data: { disable_with: "This may take upto 2 minutes... "} %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<script>
var captcha_interval;
var myCallback=function(){
document.getElementsByName('commit')[0].disabled=false;
};
var onloadCallback = function() {
document.getElementsByName('commit')[0].disabled=true;
grecaptcha.render(
document.getElementById('g-recaptcha'),
{
callback: myCallback,
sitekey: "6LcOGEgUAAAAAOxXmBVgxxxxxx"
});
};
</script>
<html>
<head>
<title>reCAPTCHA demo: Explicit render for multiple widgets</title>
<script type="text/javascript">
var verifyCallback = function(response) {
alert(response);
};
var onloadCallback = function() {
grecaptcha.render('example3', {
'sitekey' : '6LcOGEgUAAAAAOxXmBVgxxxxxx',
'callback' : verifyCallback,
'theme' : 'dark'
});
};
</script>
</head>
<body>
<!-- POSTs back to the page's URL upon submit with a g-recaptcha-response POST parameter. -->
<form action="?" method="POST">
<div id="example3"></div>
<br>
<input type="submit" value="Submit">
</form>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
async defer>
</script>
</body>
</html>
As per reference google-captcha, you are missing the
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
async defer>
</script>
after the form.
Please try adding after form and let me know

[Rails][Vue] Empty array returned when fetching the children nested within a parent component. Any idea why is this happening?

I'm using Rails 5.1 + Webpack running Vue 2. I'm fairly new to Vue, not so new to Rails.
I'm building a navbar component starting from the Bulma navbar. My objective is to be able to dynamically create links in the navbar by simply adding a navlink element nested within.
In my application.html.erb I have already created this structure (navlink nested inside navbar).
In hello_vue.js I'm initializing the Vue components and assigning them to a root element (one for the navbar and one for the rest of the content).
In navbar.vue, I'd like to iterate through all the and create a menu item accordingly. In order to do so, I added an empty navlinks data to navbar that I want to populate once it gets mounted() by fetching all its children (the nested navlink tags).
This is however not working, whenever I reference this.$children I get an empty array. Any idea why is this happening?
This is all the code.
application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>RailsVue</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'hello_vue' %>
</head>
<body>
<div id="navbar-container">
<navbar>
<navlink href="http://google.com">
Link 1
</navlink>
<navlink href="http://facebook.com">
Link 2
</navlink>
</navbar>
</div>
<%= yield %>
<%= javascript_pack_tag 'hello_vue' %>
</body>
</html>
hello_vue.js
import Vue from 'vue/dist/vue.esm'
import App from './app.vue'
import Navlink from './components/navlink.vue'
import Navbar from './components/navbar.vue'
const navbar = new Vue({
el: "#navbar-container",
components: { Navbar, Navlink }
})
const app = new Vue({
el: '#hello',
data: {
message: "Can you say hello?"
},
components: { App }
})
navbar.vue
<template>
<div>
<nav class="navbar is-transparent">
<div class="navbar-brand">
<a class="navbar-item" href="http://bulma.io">
<img src="http://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox" width="112" height="28">
</a>
<div class="navbar-burger burger" data-target="navMenuTransparentExample">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navMenuTransparentExample" class="navbar-menu">
<div class="navbar-start">
<a v-for="navlink in navlinks" class="navbar-item" :href="navlink.href">
<slot></slot>
</a>
</div>
</div>
</nav>
</div>
</template>
<script>
import Navlink from './navlink.vue'
export default {
name: "Navbar",
data: function () {
return {
navlinks: []
}
},
created: function(){
// let children = this.$children;
console.log(this.$children);
// this.navlinks = this.$children;
// this.navlinks = this.$children;
}
}
</script>
navlink.vue
<template>
<div class="navlink">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Navlink"
}
</script>
Instead of created hook, use mounted hook, you can able to get this.$children
mounted : function(){
// let children = this.$children;
console.log(this.$children);
// this.navlinks = this.$children;
// this.navlinks = this.$children;
}
You can modify components to achieve your requirement as follows
application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>RailsVue</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'hello_vue' %>
</head>
<body>
<div id="navbar-container">
<navbar>
<navlink>
<a class="navbar-item" href="http://google.com">
Link 1
</a>
</navlink>
<navlink>
<a class="navbar-item" href="http://facebook.com">
Link 2
</a>
</navlink>
</navbar>
</div>
<%= yield %>
<%= javascript_pack_tag 'hello_vue' %>
</body>
</html>
hello_vue.js
import Vue from 'vue/dist/vue.esm'
import App from './app.vue'
import Navlink from './components/navlink.vue'
import Navbar from './components/navbar.vue'
const navbar = new Vue({
el: "#navbar-container",
components: { Navbar, Navlink }
})
navbar.vue
<template>
<div>
<nav class="navbar is-transparent">
<div class="navbar-brand">
<a class="navbar-item" href="http://bulma.io">
<img src="http://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox"
width="112" height="28">
</a>
<div class="navbar-burger burger" data-target="navMenuTransparentExample">
<span></span>
<span></span>
<span></span>
</div>
</div>
<slot></slot>
</nav>
</div>
</template>
<script>
// import Navlink from './navlink.vue'
export default {
name: 'Navbar',
data: function () {
return {
navlinks: []
}
},
mounted: function () {
// let children = this.$children;
// console.log(this)
console.log(this.$children)
// console.log(this.$root.$children[0])
// this.navlinks = this.$children
// this.navlinks = this.$children;
}
}
</script>
navlink.vue
<template>
<div class="navlink">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'Navlink'
}
</script>

Resources