I need to search two addresses on the same webpage, one for location, one for correspondence. The first Google API Address works fine, I then tried duplicating the function and form modifying it, but it doesn't populate the second address, it always tries to populate the first address, can anyone tell me where I am going wrong please? Thanks for your help.
function initMap() {
const componentForm = [
'street_number',
'route',
'location',
'locality',
'administrative_area_level_2',
'postal_code',
];
const autocompleteInput = document.getElementById('location');
const options = {
types: ['(cities)'],
componentRestrictions: { country: 'gb' }
};
const autocomplete = new google.maps.places.Autocomplete(autocompleteInput);
autocomplete.addListener('place_changed', function () {
const place = autocomplete.getPlace();
if (!place.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert('No details available for input: \'' + place.name + '\'');
return;
}
fillInAddress(place);
});
function fillInAddress(place) { // optional parameter
const addressNameFormat = {
'street_number': 'short_name',
'route': 'long_name',
'locality': 'long_name',
'administrative_area_level_2': 'short_name',
'postal_code': 'short_name',
};
const getAddressComp = function (type) {
for (const component of place.address_components) {
if (component.types[0] === type) {
return component[addressNameFormat[type]];
}
}
return '';
};
document.getElementById('location').value = getAddressComp('street_number') + ' '
+ getAddressComp('route');
for (const component of componentForm) {
// Location field is handled separately above as it has different logic.
if (component !== 'location') {
document.getElementById(component).value = getAddressComp(component);
}
}
}
}
function initMapAddress2() {
const componentForm = [
'street_number',
'route',
'location',
'locality',
'administrative_area_level_2',
'postal_code',
];
const autocompleteInput = document.getElementById('location2');
const options = {
types: ['(cities)'],
componentRestrictions: { country: 'gb' }
};
const autocomplete2 = new google.maps.places.Autocomplete(autocompleteInput);
autocomplete2.addListener('place_changed', function () {
const place2 = autocomplete2.getPlace();
if (!place2.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
window.alert('No details available for input: \'' + place2.name + '\'');
return;
}
fillInAddress(place2);
});
function fillInAddress(place2) { // optional parameter
const addressNameFormat = {
'street_number2': 'short_name',
'route2': 'long_name',
'locality2': 'long_name',
'administrative_area_level_22': 'short_name',
'postal_code2': 'short_name',
};
const getAddressComp = function (type) {
for (const component of place2.address_components) {
if (component.types[0] === type) {
return component[addressNameFormat[type]];
}
}
return '';
};
document.getElementById('location2').value = getAddressComp('street_number2') + ' '
+ getAddressComp('route2');
for (const component of componentForm) {
// Location field is handled separately above as it has different logic.
if (component !== 'location2') {
document.getElementById(component).value = getAddressComp(component);
}
}
}
}
<div class="card-container">
<div class="panel">
<div>
<img class="sb-title-icon" src="https://fonts.gstatic.com/s/i/googlematerialicons/location_pin/v5/24px.svg" alt="">
<span class="sb-title">Correspondence Address</span>
</div>
<input type="text" placeholder="Search Address" id="location" />
<input type="text" placeholder="" id="street_number" />
<input type="text" placeholder="" id="route" />
<input type="text" placeholder="" id="locality" />
<div class="half-input-container">
<input type="text" class="half-input" placeholder="" id="administrative_area_level_2" />
<input type="text" class="half-input" placeholder="" id="postal_code" />
</div>
</div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=****************Zv_k&libraries=places&callback=initMap&channel=GMPSB_addressselection_v1_cA" async defer></script>
<div class="card-container">
<div class="panel">
<div>
<img class="sb-title-icon" src="https://fonts.gstatic.com/s/i/googlematerialicons/location_pin/v5/24px.svg" alt="">
<span class="sb-title">Location Address</span>
</div>
<input type="text" placeholder="Search Address" id="location2" />
<input type="text" placeholder="" id="street_number2" />
<input type="text" placeholder="" id="route2" />
<input type="text" placeholder="" id="locality2" />
<div class="half-input-container">
<input type="text" class="half-input" placeholder="" id="administrative_area_level_22" />
<input type="text" class="half-input" placeholder="" id="postal_code2" />
</div>
</div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=****************Zv_k&libraries=places&callback=initMapAddress2&channel=GMPSB_addressselection_v1_cA" async defer></script>
For anyone else having the same issue, I found a solution here Multiple Address on same page
Related
I am creating google autocomplete address form for multiple input fields. My code is work for single field. now i am changing function so it works for all location. how to avoid repeation of functions in above code for mulitple inputfield? I want to create this address functionality for multiple input box. https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform i am refer this plz need help
<div class="col-12 mb-3">
<label for="current-location" class="mb-1">Current Location</label>
<input type="text" name="locality" id="locality" class="form-control loc">
</div>
<div class="col-12 mb-3">
<label for="preferred-location" class="mb-1">Preferred Location </label>
<div class="row" id="location_row">
<div class="col-12 mb-2">
<input type="text" name="preferred_location[]" id="preferred_location_1" class="form-control loc" placeholder="Search & Select Location">
</div>
<div class="col-12 mb-2">
<input type="text" name="preferred_location[]" id="preferred_location_2" class="form-control loc" placeholder="Search & Select Location">
</div>
</div>
</div>
<script>
let autocomplete;
let address1Field;
let address2Field;
let postalField;
let inputField ;
function initAutocomplete() {
address1Field = document.querySelector("#locality");
address2Field = document.querySelector("#preferred_location_1");
autocomplete = new google.maps.places.Autocomplete(address1Field, {
componentRestrictions: { country: ["in"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
autocomplete.addListener("place_changed", fillInAddress);
autocomplete2 = new google.maps.places.Autocomplete(address2Field, {
componentRestrictions: { country: ["in"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
autocomplete2.addListener("place_changed", fillInAddress2);
}
function fillInAddress() {
const place = autocomplete.getPlace();
if(place!=undefined) {
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "locality":
document.querySelector("#locality").value = component.long_name;
break;
}
}
}
}
function fillInAddress2() {
const place = autocomplete2.getPlace();
if(place!=undefined) {
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "locality":
document.querySelector("#preferred_location_1").value = component.long_name;
break;
}
}
}
}
</script>
Here I am getting an error of handleSubmit function because ,here I am using this syntax 'function PostForm () ' instead of 'class PostForm extends Component {}',so what needs to be changed in order to work with handlesubmit function.
function PostForm () {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message
}
console.log(data)
}
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
export default PostForm;
Since it's the function component you need to define handleSubmit as a function.
For example
const handleSubmit = (e) => {
...
}
I am trying to populate a select menu in redux forms dynamically.
I've been using the debugging tools in chrome and can see that the 'departments' variable sees the array list
({departments.map(department => <option key={department} value={department}>{department}</option>)}
but the final choice list isn't populating. I'm guessing it has something to do with the renderSelectField function, but I'm not sure what I am overlooking?
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import isValidEmail from 'sane-email-validation';
class SimpleReduxForm extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.renderSelectField = this.renderSelectField.bind(this);
}
onSubmit = async (data) => {
try {
let response = await fetch('/api/getRecords', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-type': 'application/json'
}
});
let responseJson = await response.json();
//display success message to user
alert('Form successfully submitted')
return responseJson;
//reset form
} catch (error) {
alert(error);
}
}
renderInputField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<input placeholder={field.label} {...field.input} className="form-control" type={field.input.type} />
</div>
</div>
)
}
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option></select>
</div>
</div>
)
}
render() {
const { handleSubmit, pristine, reset, submitting, invalid } = this.props;
//Options for select - this should be an AJAX call to a table to get options list
const departments = ["Dept 1", "Dept 2", "Dept 3"]
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
label="Username"
name="username"
component={this.renderInputField}
type="text"
/>
<Field
label="Email"
name="email"
component={this.renderInputField}
type="email"
/>
<Field
label="Age"
name="num_field"
component={this.renderInputField}
type="text"
/>
<Field
label="Department"
name="department"
defaultselection="Select..."
component={this.renderSelectField}>
{departments.map(department => <option key={department} value={department}>{department}</option>)}
</Field>
<div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
<button type="button" className="btn btn-warning" disabled={pristine || submitting} onClick={reset}> Clear Values </button>
</div>
</form >
)
}
}
//Validate Errors Before Submission
const validate = (values) => {
//create errors object
const errors = {}
/*Example showing to check is a field contains data
* if no, submission == invalid*/
if (!values.username) {
errors.username = 'Required!'
}
/*check to see if email is provided and that submission is an actual email address*/
if (!values.email) {
errors.email = 'Required!'
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid Email!'
}
/* Example to show that the length of a field
* can be checked as part of validation */
if (values.num_field < 2) {
errors.num_field = "Must be at least 10 years old"
}
return errors
}
const mapStateToProps = state => ({
SimpleReduxForm: state.form.SimpleReduxForm
});
export default reduxForm({
validate,
form: 'SimpleReduxForm',
enableReinitialize: true,
keepDirtyOnReinitialize: true,
})(connect(mapStateToProps)(SimpleReduxForm));
I figured it out. Just in case anyone else runs into this issue. I needed to add {field.children} into the renderSelectField function. So the final function looks like:
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option>{field.children}</select>
</div>
)
}
I have this form, and I would like to send these values. I know we have to use setState() to store data but how does it work for input type="hidden"?
First question: How to store input hidden to setState ?
Second question: How to serialize data like form.serialize() ?
Third question: How to send these serialize values? Ajax or Axios, who is the better?
Here is the code:
handleSubmit(e) {
e.preventDefault();
/**
$.ajax({
url: "post.php",
type: "POST",
data: DATA,
success:function(data) {
}
});
**/
}
<form onSubmit={this.handleSubmit}>
<input type="hidden" name="action" value="login" />
<input type="email" name="email_user" placeholder="Email" />
<input type="password" name="password_user" placeholder="Mot de passe" />
<button type="submit">Login</button>
</form>
The answer is complex for all your questions.
First of all, it depends on the task: if you just want to send asynchonous request to server on form submit, you don't need to use Component state. Here is a link to the relevant section of the documentation. And use refs to access inputs data.
class FormComponent extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
// Send your ajax query via jQuery or Axios (I prefer Axios)
axios.get('your_url', {
params: {
action: this.actionInput.value,
email: this.emailInput.value,
password: this.passwordInput.value,
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<form onSubmit={this.onSubmit}>
<input type="hidden" name="action" value="login"
ref={(input) => { this.actionInput = input }} />
<input type="email" name="email_user" placeholder="Email"
ref={(input) => { this.emailInput = input }}/>
<input type="password" name="password_user" placeholder="Mot de passe"
ref={(input) => { this.passwordInput = input }}/>
<button type="submit">Login</button>
</form>
);
}
}
All data can be stored on React's state, but if you still need to have inputs on your form you can do something like this:
const handleSubmit = e => {
e.preventDefault();
const inputs = Object.values(e.target)
.filter(c => typeof c.tagName === 'string' && c.tagName.toLowerCase() === 'input')
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.value }), {});
setFormVals({ ...formVals, ...inputs });
}
See the demo below:
const Demo = () => {
const [formValues] = React.useState({});
const handleSubmit = e => {
e.preventDefault();
const inputs = Object.values(e.target)
.filter(c => typeof c.tagName === 'string' && c.tagName.toLowerCase() === 'input')
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.value }), {});
console.log(inputs);
}
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" value={formValues.name} />
<input name="email" placeholder="Email" value={formValues.email} />
<input name="hiddenInput" value="hiddenValue" type="hidden" />
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(<Demo />, document.getElementById('demo'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="demo"></div>
If you know what the inputs that you need you can do something like this:
const Demo = () => {
const formRef = React.useRef(null);
const [formValues, setFormValues] = React.useState({});
const handleChange = e => {
setFormValues({
...formValues,
[e.target.name]: e.target.value,
});
}
const handleSubmit = e => {
e.preventDefault();
setFormValues({ ...formValues, hiddenInput: formRef.current.hiddenInput.value });
}
return (
<form onSubmit={handleSubmit} ref={formRef}>
<input name="name" placeholder="Name" value={formValues.name} onChange={handleChange} />
<input name="email" placeholder="Email" value={formValues.email} onChange={handleChange} />
<input name="hiddenInput" value="hiddenValue" type="hidden" />
<button type="submit">Submit</button>
<pre>{JSON.stringify(formValues, null, 2)}</pre>
</form>
);
}
ReactDOM.render(<Demo />, document.getElementById('demo'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="demo"></div>
Answering your questions:
Since you know how to use component's state you may set the value as : <input type='text' value={this.state.foo} /> or even via props passing <input type='hidden' value={this.props.foo} />
You don't need to serialise anything at all. Use your component's local state or even a state container like Redux or Flux in order to pick the appropriate data. Take a look at this fairly simple example:
var SuperForm = React.createClass({
getInitialState() {
return {
name: 'foo',
email: 'baz#example.com'
};
},
submit(e){
e.preventDefault();
console.log("send via AJAX", this.state)
},
change(e,key){
const newState = {};
newState[key] = e.currentTarget.value;
this.setState(newState)
},
render: function() {
return (
<div>
<label>Name</label>
<input
onChange={(e) => this.change(e,'name')}
type="text"
value={this.state.name} />
<label>Email</label>
<input
onChange={(e) => this.change(e,'email')}
type="text"
value={this.state.email} />
<button onClick={this.submit}>Submit</button>
</div>
);
}});
Demo
AJAX is a set of web development techniques while Axios is a JavaScript framework. You may use jQuery, Axios or even vanilla JavaScript.
I am validating two sections of a webpage the first validation section validates however the second validator is not for some reason.
$(function(){
/* first validation - works*/
jVal = {
//validate firstName
'firstName': function(){
//appends #firstNameInfo with .info to body
$('body').append('<div id="firstNameInfo" class="info"></div>');
//create variables
var firstNameInfo = $('#firstNameInfo');
var ele = $('#firstName');
var patt = /^[a-zA-Z][a-zA-Z]{1,20}$/;
if(!patt.test(ele.val())) {
jVal.errors = true;
firstNameInfo.removeClass('correct').addClass('error');
ele.removeClass('normal').addClass('wrong');
}else{
firstNameInfo.removeClass('error').addClass('correct');
ele.removeClass('wrong').addClass('normal');
}
},
//validate lastName
'lastName': function() {
$('body').append('<div id="lastNameInfo" class="info"></div>');
var lastNameInfo = $('#lastNameInfo');
var ele =$('#lastName');
var patt = /^[a-zA-Z][a-zA-Z]{1,20}$/;
if(!patt.test(ele.val())){
jVal.errors = true;
lastNameInfo.removeClass('correct').addClass('error');
ele.removeClass('normal').addClass('wrong');
}else{
lastNameInfo.removeClass('error').addClass('correct');
ele.removeClass('wrong').addClass('normal');
}
},
//validate phone
'phone' : function(){
$('body').append('<div id="phoneInfo" class="info"></div>');
var phoneInfo = $('#phoneInfo');
var ele = $('#phone');
var patt = /^((\+?1-)?\d\d\d-)?\d\d\d-\d\d\d\d$/;
if(!patt.test(ele.val())) {
jVal.errors = true;
phoneInfo.removeClass('correct').addClass('error');
ele.removeClass('normal').addClass('wrong');
}else{
phoneInfo.removeClass('error').addClass('correct');
ele.removeClass('wrong').addClass('normal');
}
},
//validate zipcode
'zip' : function() {
$('body').append('<div id="zipInfo" class="info"></div>');
var zipInfo = $("#zipInfo");
var ele = $('#content_form #zip');
var patt = /^\d\d\d\d\d$/;
if(!patt.test(ele.val())){
jVal.errors = true;
zipInfo.removeClass('correct').addClass('error');
ele.removeClass('normal').addClass('wrong');
}else{
zipInfo.removeClass('error').addClass('correct');
ele.removeClass('wrong').addClass('normal');
}
},
//submit button code
'sendForm':function(){
if(!jVal.errors){
$('#content_form').submit();
}
},
};
$('#content_form #submit').click(function(){
var obj = $.browser.webkit ? $('body') : $('html');
jVal.errors = false;
jVal.firstName();
jVal.lastName();
jVal.phone();
jVal.zip();
jVal.sendForm();
return false;
$('#firstName').change(jVal.firstName);
$('#lastName').change(jVal.lastName);
$('#email').change(jVal.email);
$('#content_form #zip').change(jVal.zip);
});
/**** Second Validation Does Not work ******/
kVal ={
'zip' : function() {
$('body').append('<div id="Infozip" class="info"></div>');
var Infozip = $("#Infozip");
var ele = $('#form #zip');
var patt = /^\d\d\d\d\d$/;
if(!patt.test(ele.val())){
kVal.error = true;
Infozip.removeClass('correct').addClass('error');
ele.removeClass('normal').addClass('wrong');
}else{
Infozip.removeClass('error').addClass('correct');
ele.removeClass('wrong').addClass('normal');
}
},
//submit button code
'send':function(){
if(!kVal.errors){
$('#form').submit();
}
},
};
$('#form button#submit').click(function(){
var obj = $.browser.webkit ? $('body') : $('html');
kVal.errors = false;
kVal.zip();
kVal.send();
return false;
$('#form #zip').change(kVal.zip);
});
}); /*main function closer*/
HTML FOR FIRST Validation -WORKING -
<div id="content_form">
<p>
<label class="block">
<input type="text" id="firstName" type="firstName" autocomplete="on" value="first name">
</label>
<label class="block">
<input type="text" id="lastName" type="lastName" autocomplete="on" value="last name">
</label>
<label class="block">
<input type="text" id="phone" type="phone" autocomplete="on" value="phone">
</label>
<label class="block">
<input type="text" id="zip" type="zip" autocomplete="on" value="zip code">
</label>
<button type="submit" id="submit" value="Submit" title="submit">submit</button>
</p>
</div><!-- end form -->
HTML FOR SECOND Validation
<div id="form">
<p>
<label class="block">
<input type="text" id="zip" type="zip" autocomplete="on" value="zip code">
</label>
<button type="submit" id="submit" value="CHECK NOW" title="check now">check now</button>
</p>
</div><!-- end form -->
You have the same id on both zip fields, which is probably causing your problems. The docs for the jQuery #id selector has this to say;
Each id value must be used only once within a document. If more than
one element has been assigned the same ID, queries that use that ID
will only select the first matched element in the DOM. This behavior
should not be relied on, however; a document with more than one
element using the same ID is invalid.
That is, your selection in $('#form #zip').change(kVal.zip); will not use the hierarchy under #form to find #zip, it will still find the first instance in the entire DOM.