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

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.

Related

Rails 7 toastr using importmaps

Im trying to use toastr to display a message. Im using rails 7 but Im unsure if i have it imported and implemented correctly
import { Controller } from "#hotwired/stimulus";
import { initializeApp } from 'firebase';
import { getMessaging, getToken, onMessage } from "firebase-messaging";
// import { onBackgroundMessage } from "firebase-messaging-sw";
import * as toastr from 'toastr';
onMessage(messaging, (payload) => {
alert(`${ payload.notification.title }\n ${ payload.notification.body }`);
toastr['success']("Message will come here", "Title");
});
That last line is where nothing happens. the alert shows up as expected
pin "toastr", to: "https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"
That is my pin statement in importmap.rb
<head>
<title>NitroCabs</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/firebase-messaging-sw.js');
});
}
</script>
<%= favicon_link_tag asset_path('firebase-logo.png') %>
That is my head in application.html.erb

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!

Rails geocoding/Mapbox GL JS "missing CSS declarations for Mapbox GL JS" "_typeof is not defined"

I want to display my MapBox map into may <div id="map">... but I'm getting two errors: >This page appears to be missing CSS declarations for Mapbox GL JS... and >_ReferenceError: _typeof is not defined
I read a lot of issues about it on the web but still doesn't work :/
views/layouts/applications.html.erb
<html>
<head>
<title>MarryApp</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<!-- <link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.2/mapbox-gl.css" rel="stylesheet" /> -->
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= stylesheet_pack_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
</head>
<body>
<%= render 'shared/navbar' %>
<%= render 'shared/flashes' %>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 p-0">
<%= yield %>
</div>
</div>
</div>
<%= javascript_pack_tag 'application' %>
</body>
</html>
app/javascript/packs/application.js
import 'mapbox-gl/dist/mapbox-gl.css';
import { initMapbox } from '../plugins/mapbox';
initMapbox();
app/javascript/plugins/mapbox.js
import mapboxgl from 'mapbox-gl';
const initMapbox = () => {
const mapElement = document.getElementById('map');
if (mapElement) { // only build a map if there's a div#map to inject into
mapboxgl.accessToken = mapElement.dataset.mapboxApiKey;
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v10'
});
}
console.log(mapElement.dataset.mapboxApiKey)
};
export { initMapbox };

Why does my stripe elements form not work 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
}

Why am i getting an error Uncaught TypeError: Cannot read property 'insertAdjacentHTML' of null at renderHTML

How do I get my information to render and not display an error? I was following a video and I'm also having a few other console errors that may or may not be affecting it also.
var bitprice = document.getElementById('bitprice');
$( document ).ready(function() {
var bitcoin = new XMLHttpRequest();
bitcoin.open('GET', "https://api.coindesk.com/v1/bpi/currentprice.json");
bitcoin.onload = function() {
// console.log(bitcoin.responseText);
var bitdata = JSON.parse(bitcoin.responseText);
// console.log(bitdata.bpi.USD.rate);
renderHTML(bitdata.bpi.USD.rate);
};
bitcoin.send();
});
function renderHTML(data) {
bitprice.insertAdjacentHTML('beforeend',"test")
}
<!DOCTYPE html>
<html>
<head>
<title>Bitview</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %> (Inside the yield is as follows <div id="bitprice"></div>)
<script type="text/javascript" src="app/assets/javascripts/js.js"></script>
</body>
</html>
why is var bitprice outside of the function that is using it? Try defining it inside the function and it should work.
There's a thing to learn about called "scope". The way javascript works - something outside the function might not be visible inside the function because of the way scope works. It's worth googling about for a good article or two.

Resources