Mohamed Atef
3 Jul 2021
React js
Google Maps or addresses API is a service from Google Could PlatformWhich allow you to get the key and start searching or fetching data and details about places or addresses using google, and I am going to show you how to use this service with React js, Formik, Material UI
In the beginning, I searched and tried a lot of Packages but for sorry around 90% of the packages are not supporting Material UI and it's not gonna be simple to use it with Formik so I created a custom way to figure it out, it's really simple, let's start setting up the google places API all you need to do is just getting your project key, From Google Maps Click on enable
Then from Credentials > Create Credentials and choose API,
When you finish this step google will ask you to copy your API key copy it and do the next step
You need to load the library of google places into your website, we gonna do this from the App folder > public > index.html then add this script at the end of the head tag
<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&language=en&libraries=places"></script>
Now you need to make sure that you installed Formik & Material UI & Material Lab
Install them in case you don't have them
npm install @material-ui/core
Install Formik
npm install formik --save
Material UI Lab (to import the Autocomplete input)
npm install @material-ui/lab
Now you need to set up two components include Google functions to make it easy to use them in the parent component
First file: GoogleAutoCompelete.js
const GoogleAutoCompelete = async text =>
new Promise((resolve, reject) => {
if (!text) {
return reject("Need valid text input")
}
// for use in things like GatsbyJS where the html is generated first
if (typeof window === "undefined") {
return reject("Need valid window object")
}
try {
new window.google.maps.places.AutocompleteService().getQueryPredictions(
{ input: text, componentRestrictions: { country: "us" }, fields: ["address_components", "geometry"], },
resolve
)
} catch (e) {
reject(e)
}
})
export default GoogleAutoCompelete;
Second file: GetPostalCode.js
const GetPostalCode = async placeId =>
new Promise((resolve, reject) => {
if (!placeId) reject("placeId not provided")
try {
new window.google.maps.places.PlacesService(
document.createElement("div")
).getDetails(
{
placeId,
fields: ["address_components"],
},
details => {
return resolve(details)
}
)
} catch (e) {
reject(e)
}
})
export default GetPostalCode;
Then you have to include them in the main component
import GoogleAutoCompelete from './Google/GoogleAutoCompelete';
import GetPostalCode from './Google/GetPostalCode';
Now create the form with the functions to use the components
<Formik
enableReinitialize={true}
initialValues={{
companyAddress: '',
zipCode: '',
city: '',
stateOfAddress: '',
submit: null
}}
validationSchema={Yup.object().shape({
companyAddress: Yup.string().max(255).required('Company address is required'),
zipCode: Yup.string().max(255),
city: Yup.string().max(255),
stateOfAddress: Yup.string().max(255),
})}
onSubmit={async (values, {
setErrors,
setStatus,
setSubmitting,
}) => {
try {
console.log(values);
} catch (err) {
console.error(err);
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
}
}}
>
{({
errors,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
touched,
values,
setFieldValue
}) => (
<form
onSubmit={handleSubmit}
{...rest}
>
<Grid container>
<Grid item md={6} xs={12}>
<Box mt={3} px={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Autocomplete
options={addresses.map((option) => option.description)}
// closeIcon= { () => { return; } }
onInputChange={(event, value) => { changeAddress(value, setFieldValue); }}
autoComplete={false}
renderInput={(params) => (
<TextField {...params} label="Company Address" name="companyAddress" value={values.companyAddress} onChange={(value) => { handleChangeAddress(value); }} variant="outlined" />
)}
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.zipCode && errors.zipCode)}
fullWidth
helperText={touched.zipCode && errors.zipCode}
label="Zip Code"
name="zipCode"
onBlur={handleBlur}
onChange={handleChange}
value={values.zipCode}
variant="outlined"
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.city && errors.city)}
fullWidth
helperText={touched.city && errors.city}
label="City"
name="city"
onBlur={handleBlur}
onChange={handleChange}
value={values.city}
variant="outlined"
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.stateOfAddress && errors.stateOfAddress)}
fullWidth
helperText={touched.stateOfAddress && errors.stateOfAddress}
label="State (Administrative Area)"
name="stateOfAddress"
onBlur={handleBlur}
onChange={handleChange}
value={values.stateOfAddress}
variant="outlined"
/>
</Grid>
</Grid>
{Boolean(touched.tags && errors.tags) && (
<Box mt={2}>
<FormHelperText error>
{errors.tags}
</FormHelperText>
</Box>
)}
{Boolean(touched.startDate && errors.startDate) && (
<Box mt={2}>
<FormHelperText error>
{errors.startDate}
</FormHelperText>
</Box>
)}
{Boolean(touched.endDate && errors.endDate) && (
<Box mt={2}>
<FormHelperText error>
{errors.endDate}
</FormHelperText>
</Box>
)}
</Box>
<Box
mt={12}
display="flex"
>
<Box flexGrow={1} />
<Button
color="secondary"
disabled type="submit"
variant="contained"
size="large"
>
Submit
</Button>
</Box>
</Grid>
</Grid>
</form>
)}
</Formik>
All we need to use the Google components
const [addresses, setAddresses] = useState([]);
const handleChangeAddress = async (searchValue) => {
const results = await GoogleAutoCompelete(searchValue.target.value);
if (results) {
setAddresses(results);
}
}
const changeAddress = async (value, setFieldValue) => {
let result = null;
for(let x = 0; x < addresses.length; x++){
if(value === addresses[x].description){
result = await GetPostalCode(addresses[x].place_id);
// Get Zip code
}
}
if(!result){ return ; }
setFieldValue('companyAddress', value);
let postcode = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'postal_code'){
postcode = result.address_components[i].long_name;
}
}
setFieldValue('zipCode', postcode);
// Get city
let city = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'locality'){
city = result.address_components[i].long_name;
}
}
if(!city){
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_2'){
city = result.address_components[i].long_name;
}
}
}
setFieldValue('city', city);
// Get State
let state = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_1'){
state = result.address_components[i].long_name;
}
}
if(!state){
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_2'){
state = result.address_components[i].long_name;
}
}
}
setFieldValue('stateOfAddress', state);
}
That's all and here is the content of the main component (AutoFillForm.js)
import React, { useState, useEffect } from 'react';
import * as Yup from 'yup';
import { Formik } from 'formik';
import {
Box,
Button,
FormHelperText,
Grid,
TextField,
} from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import GoogleAutoCompelete from './Google/GoogleAutoCompelete';
import GetPostalCode from './Google/GetPostalCode';
const AutoFillForm = ({
className,
setData,
data,
projectId,
...rest
}) => {
const [addresses, setAddresses] = useState([]);
const handleChangeAddress = async (searchValue) => {
if(!searchValue.target.value){
return null;
}
const results = await GoogleAutoCompelete(searchValue.target.value);
if (results) {
setAddresses(results);
}
}
const changeAddress = async (value, setFieldValue) => {
let result = null;
for(let x = 0; x < addresses.length; x++){
if(value === addresses[x].description){
result = await GetPostalCode(addresses[x].place_id);
// Get Zip code
}
}
if(!result){ return ; }
setFieldValue('companyAddress', value);
let postcode = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'postal_code'){
postcode = result.address_components[i].long_name;
}
}
setFieldValue('zipCode', postcode);
// Get city
let city = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'locality'){
city = result.address_components[i].long_name;
}
}
if(!city){
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_2'){
city = result.address_components[i].long_name;
}
}
}
setFieldValue('city', city);
// Get State
let state = null;
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_1'){
state = result.address_components[i].long_name;
}
}
if(!state){
for(let i = 0; i < result.address_components.length; i++){
if(result.address_components[i].types[0] === 'administrative_area_level_2'){
state = result.address_components[i].long_name;
}
}
}
setFieldValue('stateOfAddress', state);
}
//We can use this function to disable the browser auto complete from the fields because it looks really annoying
useEffect(() => {
window.document.querySelector('input[name="companyAddress"]').setAttribute('autocomplete', 'disable');
window.document.querySelector('input[name="companyAddress"]').setAttribute('aria-autocomplete', 'off');
window.document.querySelector('input[name="zipCode"]').setAttribute('autocomplete', 'disable');
window.document.querySelector('input[name="zipCode"]').setAttribute('aria-autocomplete', 'off');
window.document.querySelector('input[name="city"]').setAttribute('autocomplete', 'disable');
window.document.querySelector('input[name="city"]').setAttribute('aria-autocomplete', 'off');
window.document.querySelector('input[name="stateOfAddress"]').setAttribute('autocomplete', 'disable');
window.document.querySelector('input[name="stateOfAddress"]').setAttribute('aria-autocomplete', 'off');
}, []);
return (
<Formik
enableReinitialize={true}
initialValues={{
companyAddress: '',
zipCode: '',
city: '',
stateOfAddress: '',
submit: null
}}
validationSchema={Yup.object().shape({
companyAddress: Yup.string().max(255).required('Company address is required'),
zipCode: Yup.string().max(255),
city: Yup.string().max(255),
stateOfAddress: Yup.string().max(255),
})}
onSubmit={async (values, {
setErrors,
setStatus,
setSubmitting,
}) => {
try {
console.log(values);
} catch (err) {
console.error(err);
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
}
}}
>
{({
errors,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
touched,
values,
setFieldValue
}) => (
<form
onSubmit={handleSubmit}
{...rest}
>
<Grid container>
<Grid item md={6} xs={12}>
<Box mt={3} px={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Autocomplete
options={addresses.map((option) => option.description)}
// closeIcon= { () => { return; } }
onInputChange={(event, value) => { changeAddress(value, setFieldValue); }}
autoComplete={false}
renderInput={(params) => (
<TextField {...params} label="Company Address" name="companyAddress" value={values.companyAddress} onChange={(value) => { handleChangeAddress(value); }} variant="outlined" />
)}
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.zipCode && errors.zipCode)}
fullWidth
helperText={touched.zipCode && errors.zipCode}
label="Zip Code"
name="zipCode"
onBlur={handleBlur}
onChange={handleChange}
value={values.zipCode}
variant="outlined"
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.city && errors.city)}
fullWidth
helperText={touched.city && errors.city}
label="City"
name="city"
onBlur={handleBlur}
onChange={handleChange}
value={values.city}
variant="outlined"
/>
</Grid>
<Grid item xs={12}>
<TextField
error={Boolean(touched.stateOfAddress && errors.stateOfAddress)}
fullWidth
helperText={touched.stateOfAddress && errors.stateOfAddress}
label="State (Administrative Area)"
name="stateOfAddress"
onBlur={handleBlur}
onChange={handleChange}
value={values.stateOfAddress}
variant="outlined"
/>
</Grid>
</Grid>
{Boolean(touched.tags && errors.tags) && (
<Box mt={2}>
<FormHelperText error>
{errors.tags}
</FormHelperText>
</Box>
)}
{Boolean(touched.startDate && errors.startDate) && (
<Box mt={2}>
<FormHelperText error>
{errors.startDate}
</FormHelperText>
</Box>
)}
{Boolean(touched.endDate && errors.endDate) && (
<Box mt={2}>
<FormHelperText error>
{errors.endDate}
</FormHelperText>
</Box>
)}
</Box>
<Box
mt={12}
display="flex"
>
<Box flexGrow={1} />
<Button
color="secondary"
disabled type="submit"
variant="contained"
size="large"
>
Submit
</Button>
</Box>
</Grid>
</Grid>
</form>
)}
</Formik>
);
};
export default AutoFillForm;
If you have any questions let me know in the comments below
Thanks
Clement Gitonga
19 Mar 2022
I got an error “Expected to return a value at the end of arrow function” on this line in GoogleAutoComplete
new Promise((resolve, reject) => {
Clement Gitonga
23 Mar 2022
I figured out the solution to the error above. Thank you for this blog post I was able to solve a major problem. in addition I used the same concept to get Distance between two points.
Arun Chapagain
1 Aug 2022
Can you provide git hub link for this?
© 2024 Copyrights reserved for web-brackets.com