I am working with googles api places for address autocomplete suggestions.
This is my simple working code:
<!DOCTYPE html>
<html>
<body>
<form>
<input id="customerAddress" name="customerAddress" required autocomplete="off" />
<input id="zip" name="zip" required readonly />
<input id="city" name="city" required readonly />
</form>
<script
src="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&callback=initAutocomplete&libraries=places"
async
></script>
<script>
let autocomplete;
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(document.querySelector("#customerAddress"), {
componentRestrictions: { country: ["de", "DE"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
autocomplete.addListener("place_changed", fillInAddress);
}
function fillInAddress() {
const place = autocomplete.getPlace();
var address = ""
var streetNumber = ""
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "route": {
address = component.long_name;
break;
}
case "street_number": {
streetNumber = component.long_name
break;
}
case "postal_code": {
document.querySelector("#zip").value = component.long_name;
break;
}
case "locality":
document.querySelector("#city").value = component.long_name;
break;
}
}
document.querySelector("#customerAddress").value = address +" "+streetNumber
}
</script>
</body>
</html>
Now I would realize the following situation:
the customer should write his address into the field "customerAddress" to get the suggestions, but the address have to include a street number. how can I restrict this?
The form should only be able to be submitted, if the customer selected a suggestions from the autocomplete list. If he / she write manually a address, which doesn't is from the autocomplete list, it has to be an error
Well, I think the best solution would be to have separate hidden input where you would put selected address which you would run validation against, if it is not defined you simply return error.
As for having street number you can also run the validation of selected Place. If it has selected type street_number it is valid, if not it's not.
I am not sure if you can disable Google to restricts search results to only addresses with street number.
Related
I need to remove the partial postcode (in the red box) from the suggestions dropdown on autocomplete, so that users can only select a full postcode.
How do I achieve this or does anyone have a better suggestion?
I think everything works as it should but my client thinks to be able to select a partial postcode is too confusing.
This is my code:
let autocomplete;
let address1Field;
let postalField;
function initAutocomplete() {
address1Field = document.querySelector("#ship-address");
postalField = document.querySelector("#postcode");
autocomplete = new google.maps.places.Autocomplete(address1Field, {
componentRestrictions: {
country: ["gb"]
},
fields: ["address_components", "geometry"],
});
address1Field.focus();
autocomplete.addListener("place_changed", fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
const place = autocomplete.getPlace();
let address1 = "";
let postcode = "";
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "street_number":
{
address1 = `${component.long_name} ${address1}`;
break;
}
case "route":
{
address1 += component.short_name;
break;
}
case "sublocality_level_1":
{
address1 += component.long_name;
break;
}
case "postal_code":
{
postcode = `${component.long_name}${postcode}`;
break;
}
case "postal_code_suffix":
{
postcode = `${postcode}-${component.long_name}`;
break;
}
case "postal_town":
document.querySelector("#postal_town").value = component.long_name;
break;
case "locality":
// document.querySelector("#locality").value = component.long_name;
break;
case "administrative_area_level_1":
{
// document.querySelector("#state").value = component.short_name;
break;
}
case "administrative_area_level_2":
document.querySelector("#county").value = component.long_name;
break;
}
}
address1Field.value = address1;
postalField.value = postcode;
document.getElementById('details').innerHTML = '<input name="latitude" type="hidden" id="latitude" value="' + place.geometry.location.lat() + '"><input name="longitude" type="hidden" id="longitude" value="' + place.geometry.location.lng() + '">';
}
window.initAutocomplete = initAutocomplete;
There is no easy way to do this, because those partial postal codes kind of are postal codes; they are postal code prefixes which is kind of a postal code in the Places API.
You can file a feature request for Place Autocomplete to support excluding certain types. Such a feature would allow you to request predictions including types=postal_code and excluding types=postal_code_prefix.
Currently, requesting predictions with types=postal_code will include predictions with types: ["postal_code_prefix", "postal_code"].
Without such a feature, the closest to the desired effect could be building your own widget, retrieve predictions using the Place Autocomplete Service and discarding those predictions withtypes: ["postal_code_prefix", "postal_code"]. There are 2 significant problems with this approach:
Probably not compliant with clause 3.2.3.(j) of the Google Maps Platform Terms of Service: (j) No Modifying Search Results Integrity. Customer will not modify any of the Google Maps Core Services’ search results.. You would need to seek legal advice about this.
The resulting user experience would be that typing rg1 produces only one prediction (RG1 8EQ), or possible no predictions at all.
All the above might be too much hassle considering that as soon as a blank space is added to the input (e.g. "rg1 ") most of the predictions will be for full precision postal codes, and as soon as an additional character is added (e.g. "rg1 1") all the predictions will be for full precision postal codes.
This is the intended behavior, users are expected to disambiguate short inputs like rg1 by adding a character, a blank space can often make a big difference.
Objective:
build a prechat form in google apps scripts so this can be used in a google site as a webapp so that it would take a user's name, lastname, phone and email and pass those parameters to the freshchat javascript snippet, so when the chat is initiated, the user's info can be seen in the freshchat tool.
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<title>Chat form</title>
</head>
<body>
<h1>Chatea Con Nosotros</h1>
<form>
<input type="text" id="fname" placeholder="Nombre">
<input type="text" id="lname" placeholder="Apellido">
<input type="text" id="email" placeholder="Email">
<input type="text" id="phone" placeholder="Teléfono">
<input type="button" value="Iniciar Chat" id="btnStartChat"/>
</form>
<?!= include("index_js"); ?>
</body>
</html>
index_js
<script>
//global variables
var ffname = '';
var flname= '';
var femail = '';
var fphone = '';
//freshchat code starts ------------------------
function initFreshChat() {
window.fcWidget.init({
token: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx",
host: "https://wchat.freshchat.com",
open: true,
externalId: "john.doe1987", // user’s id unique to your system
firstName: ffname, // user’s first name
lastName: flname, // user’s last name
email: femail, // user’s email address
phone: fphone, // phone number without country code
phoneCountryCode: "+1" // phone’s country code
});
}
function initialize(i,t){
var e;i.getElementById(t)?initFreshChat():((e=i.createElement("script")).id=t,e. async=!0,e.src="https://wchat.freshchat.com/js/widget.js",e.onload=initFreshChat,i.head.appendChild(e))
}function initiateCall(){
initialize(document,"Freshdesk Messaging-js-sdk")
}
//freshchat code ends ---------------------
document.getElementById("btnStartChat").addEventListener("click", getFormValues);
function getFormValues(){
try{
ffname = document.getElementById("fname").value;
flname = document.getElementById("lname").value;
femail = document.getElementById("email").value;
fphone = document.getElementById("phone").value;
window.addEventListener("load", initiateCall());
}
catch(e){
console.log('error here: ' + e.message);
}
}
</script>
code.gs
function doGet(){
var page = HtmlService.createTemplateFromFile('index');
return page.evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL).setHeight(400).setWidth(100);
//return page.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
//funtion to append other files to htmlservice file
function include(filename){
//return the content of an html file
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
Problem:
the problem is that I'm not sure how to get the freshchat function to be initiated only once I have populated the global variables with input data from the form.
if I manually enter data within the window.fcWidget.init({...}) area it works nicely but I'm not sure how to do it programatically upon a button click.
sources consulted:
https://support.freshchat.com/support/solutions/articles/233349-setting-up-a-pre-chat-form-on-freshdesk-messaging
https://developers.freshchat.com/web-sdk/#customisation-wgt
Thank you.
I just followed the instructions of the first link and customized the labels. It it works within a Google Sites embed <> box.
One thing that you might be missing is that this kind of widgets have several security layers, so it might be possible that using custom HTML forms is not supported (the referred resources doesn't include instructions to use them).
Code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index');
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script src="https://snippets.freshchat.com/js/fc-pre-chat-form-v2.min.js"></script>
<script>
var preChatTemplate = {
//Form header color and Submit button color.
mainbgColor: '#0aa4db',
//Form Header Text and Submit button text color.
maintxColor: '#fff',
//Chat Form Title
heading: 'GadgetGod',
//Chat form Welcome Message
textBanner: 'We can\'t wait to talk to you. But first, please take a couple of moments to tell us a bit about yourself.',
//Submit Button Label.
SubmitLabel: 'Start Chat',
//Fields List - Maximum is 5
//All the values are mandatory and the script will not work if not available.
fields : {
field1 : {
//Type can be either text or title
type: "title",
//Label for Field Title, can be in any language
label: "Nombre",
//Field ID for Title
fieldId: "title",
//Required "yes" or "no"
required: "yes",
//Error text to be displayed
error: "Please Enter a valid Title"
},
field2 : {
//Type for Name - Do not Change
type: "name",
//Label for Field Name, can be in any language
label: "Apellido",
//Default - Field ID for Name - Do Not Change
fieldId: "name",
//Required "yes" or "no"
required: "yes",
//Error text to be displayed
error: "Please Enter a valid name"
},
field3 : {
//Type for Email - Do Not Change
type: "email",
//Label for Field Email, can be in any language
label: "Email",
//Default - Field ID for Email - Do Not Change
fieldId: "email",
//Required "yes" or "no"
required: "yes",
//Error text to be displayed
error: "Please Enter a valid Email"
},
field4 : {
//Type for Phone - Do Not Change
type: "phone",
//Label for Field Phone, can be in any language
label: "Teléfono",
//Default - Field ID for Phone - Do Not Change
fieldId: "phone",
//Required "yes" or "no"
required: "yes",
//Error text to be displayed
error: "Please Enter a valid Phone Number"
},
field5 : {
//Type for Dropdown
type: "dropdown",
//Label for Field Dropdown, can be in any language
label: "Plan",
//Field ID for Plan Dropdown
fieldId: "plan",
//Required "yes" or "no"
required: "yes",
//Error text to be displayed
error: "Please select an option",
//Options for the Dropdown field
options: ['Sprout','Blossom','Garden','Estate','Forest']
}
}
};
window.fcSettings = {
token: "WEB_CHAT_TOKEN",
host: "https://wchat.freshchat.com",
config: {
cssNames: {
//The below element is mandatory. Please add any custom class or leave the default.
widget: 'custom_fc_frame',
//The below element is mandatory. Please add any custom class or leave the default.
expanded: 'custom_fc_expanded'
}
},
onInit: function() {
console.log('widget init');
fcPreChatform.fcWidgetInit(preChatTemplate);
}
};
</script>
<script src="https://wchat.freshchat.com/js/widget.js" async></script>
<body>
</body>
</html>
I'm assuming most of this code is verbatim from freshchat? I don't have access to a freshchat account but the connection is triggered on this line:
window.addEventListener?window.addEventListener("load",initiateCall,!1):window.attachEvent("load",initiateCall,!1);
So what is happening here is basically an if statement (a ternary if https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) so if a listener can be added to the window, then add the event listener, and if not, attach the event to the window (this is probably a way to accommodate different types and version of browsers, and may not be needed for more modern browsers). More info in this: What is the difference between addEventListener and attachEvent?
So if you want to wait until the user has entered details, you will need to move this to where you are checking for the existence of those details:
function getFormValues(){
try{
ffname = document.getElementById("fname").value;
flname = document.getElementById("lname").value;
femail = document.getElementById("email").value;
fphone = document.getElementById("phone").value;
window.addEventListener("load", initiateCall());
}
catch(e){
console.log('error here: ' + e.message);
}
}
You don't really need the try/catch for getting the form info, but you should check that it is valid input (i.e. that an email address is formatted correctly, and that a name is given and not left blank).
You might also want to disable the button after the user has pressed it once, and re-enable it if there is an error, to prevent excessive calls to the API (which you might have to pay for). Another way to prevent this is to provide the user with an animation to show that something is happening while they wait for the chat to initiate, but these are out of the scope of this question.
I am trying to create a page where the websites will ask the user to input their zip code. Once the user inputs their zip code website should redirect them to their zip codes homepage. Now I have 10 different zip codes that the user can input. So I created a function for ONE zip code, but now I am stuck, I am not sure if there is a way to input all zip codes in one function or should I create one function per zip code. Thank you for your help.
<script>
function ver(){
var eje=document.getElementById('zip_code').value;
var che=document.getElementById('lugar').value;
var cheje = che.toLowerCase();
if( (eje == 11385 )||(cheje=="ridgewood" ) || (cheje=="glendale") || (cheje=="flushing")){
window.location.assign("c:/users/lui/desktop/fluidimaging.html")
}
else{ alert("you did not enter a city or a zip code"); }
}
</script>
<HTML>
<form>
<legend>Please Enter Your City or Zip Code</legend> <label for ="shippingName">City:
</label> <input type = "text" name = "Name" pattern="[A-Za-z]+" id="lugar" <br/>
<label for = "billingzip">Zip code:</label> <input type = "text" name = "zip" id =
"zip_code" pattern = "[0-9]{5}" required><br/> </form>
<input type = "submit" onclick="ver()" class="submit" value = "Verify"/>
In this case, I would like to suggest that you the following solution.
var zipCodeApps = {
90507: 'https://app.ca.com',
90890: 'https://app.ny.com'
}
This can be obtained from a REST API call to your application's backend service.
The next step would be to get the user's preferred/ location zip code
var eje=document.getElementById('zip_code').value
now you can do a lookup in the above object model like
var zipCodeBasedAppUrl = zipCodeApps[eje];
if(undefined !== zipCodeBasedAppUrl) window.location.href = zipCodeBasedAppUrl;
This way your code looks nice, if you want to get the zip code specific URL for an input from the REST API, that would also be a good option considering the level of security and the sensitivity of the data your application has, choose the right approach.
HTH
function ver() {
var eje = document.getElementById('zip_code').value;
var che = document.getElementById('lugar').value;
var cheje = che.toLowerCase();
if (eje == 11385) {
window.location.assign("c:/users/lui/desktop/fluidimaging.html");
} else if(eje == 11386) {
window.location.assign("c:/users/lui/desktop/xyz.html");
} else {
alert("you did not enter a city or a zip code");
}
}
I am trying to load google autocomplete on two places in my page. One in the nav bar for searching the site and the second for the user to create an event by entering the address. I can get either one or the other working. And once I got both working but then it wouldn't fill in the info. I'd love to know what I am doing wrong, I have been banging my head against this for a hours now.
My inputs are
<input class="form-control" id="autocomplete" type="text" placeholder="Search..." name="term">
and
<input id="autocomplete2" placeholder="Enter address to store" type="text" autocomplete="off"></input>
and my js file
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')), {
types: ['geocode']
});
autocomplete.addListener('place_changed', addlatlong);
autocomplete2 = new google.maps.places.Autocomplete(
document.getElementById('autocomplete2'), {
types: [ 'geocode' ]
});
autocomplete.addListener('place_changed', fillInAddress);
}
function addlatlong() {
var place = autocomplete.getPlace();
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
document.getElementById("latitude").value = latitude;
document.getElementById("longitude").value = longitude;
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
document.getElementById("latbox").value = latitude;
document.getElementById("lngbox").value = longitude;
console.log(`${latitude}`)
console.log(`${longitude}`)
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
Well first off, let's make sure those inputs are identical other than their ID's.
Here's a .slim example from an old project of mine:
form style="width:300px;margin: 0 auto;" action="/instructions" method="POST"
strong
p.input-label I'm currently at...
input style="width:300px;" id="autocomplete" placeholder="Enter your current address" onFocus="geolocate()" type="text" name="current_address" required="required"
strong
p.input-label And I need to get to...
input style="width:300px;" id="autocomplete2" placeholder="Enter your destination address" onFocus="geolocate()" type="text" name="destination_address" required="required"
input.button style="width:300px;" type="submit" value="What's the plan?"
Your inputs that you shared have different fields, some missing from the other, etc. Your first step should be making them identical in every way possible, and then slowly shifting one of them to your desired state.
I see you're using an older version of javascript, too. If possible you may want to consider refactoring to es5+
At the top you declare placeSearch and autocomplete, but not autocomplete2. This is another example of what I was suggesting earlier, about stepping through your code and ensuring that both inputs are being treated/created the exact same.
I see initAutocomplete is declared but never run. Is this intentional? Does the google module automatically call that function?
It seems to me like addlatlong() and fillInAddress() are the two functions you intend to use, one for autocomplete and the other for autocomplete2, but you reference autocomplete in both of them.
All in all, I think you've been messing with the code so much to try and make it work that you've lost sight of how it should be written in the end.
Perhaps starting a new file, fresh, and taking it one small step at a time might be the best way to figure out how to solve this.
Get one input working fully, as expected, and then add the next one in, step-by-step, the same way you implemented the first.
Good luck!
I'm trying to embed the google maps autocompletion API into my project.
I did everything like it was written in the documentation, but it returns me this error : Uncaught InvalidValueError: initAutocomplete is not a function. Sometimes, when I reload 3 times the page, it finally works, which is quite strange...
Does somebody know where the problem could come from ?
Here is the link where is the test that I did : http://www.dubair.ie/en/maps
Here is the code of the page (the link to the key script is in the head which is in an other file) :
{% extends "PlatformBundle::body.html.twig" %}
{% block content %}
<div id="locationField" class="form-group col-xs-12">
<textarea id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"></textarea>
</div>
<div id="details" class="hidden">
<input disabled id="street_number"/>
<input disabled id="route"/>
<input disabled id="locality"/>
<input disabled id="postal_code"/>
<input disabled id="country"/>
</div>
<script>
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
// administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('autocomplete')),
{types: ['geocode'], componentRestrictions: {country: 'ie'}});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
$('#details').removeClass('hidden').hide().slideDown('slow');
for (var component in componentForm) {
document.getElementById(component).value = '';
// document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
{% endblock %}
Thank you for your help
I think it's because you call API in the header, but you have the initAutocomplete function in the body of the HTML document. Sometimes the body is not yet loaded, sometimes it is.
Just put this code:
<script src="https://maps.googleapis.com/maps/api/js?key=....&libraries=places&callback=initAutocomplete" async defer></script>
right BEFORE </body> tag (and remove it from header). So it would be the last element in the body of the HTML document. This way initAutocomplete should always be available when the API is loaded.
I wonder how you get this error, I also wonder how it may work sometimes.
I get this error: InvalidValueError: not an instance of HTMLInputElement
The error should be clear: You use a <textarea> , but the API requires an <input>
Replace:
<textarea id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"></textarea>
with
<input id="autocomplete" placeholder="Enter your address" onFocus="geolocate()" type="text" autofocus class="form-control"/>