How to make Address Autofill forms

Address autofill form example
Address autofill form example

Autofill forms simplify the collection of Postal Addresses, which are required for many applications. For example, for payment methods to authorize transactions, a layer of security is necessary to prevent theft or fraud. Another example is delivery addresses fill in to ship goods and parcels.

Why is collecting addresses a challenge?

Here are some reasons that address collection can be a challenge:

  • even the largest address search service in the world does not contain every address;
  • new streets and buildings are always added with a delay;
  • in addition, for deliveries, it is crucial to get the exact location coordinates of a customer's address.

So when you're collecting addresses, it's important to be more permissive and flexible. For example, consider accepting addresses customers provide even if they're not verified, and allow your customers to set a custom location.

This tutorial shows three examples of address collection forms using Geoapify's Address Autocomplete. The proposed location autocomplete forms will let users add addresses and corresponding locations using auto-fill. Also, we'll show you how to verify the provided addresses.

For this tutorial, we'll use the @geoapify/geocoder-autocomplete library. It can be installed with NPM or added as a script to your web application.

Address form 1: Collecting Addresses and Displaying on A Map

This code sample includes a search bar that completes addresses and shows their locations on a map:

  • Upon entering the street name, it shows the address suggestions in a dropdown list.

  • If the street or house number is not verified, it will show the appropriate text message.

  • If the house number or street is missing in the database, it asks the user to specify its location on the map by dragging a marker to the specific location.

Adding the location autocomplete input:

const autocompleteInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("autocomplete"),
  myAPIKey, {
    lang: 'en',
    allowNonVerifiedHouseNumber: true,
    allowNonVerifiedStreet: true,
    skipDetails: true
  });

The @geoapify/geocoder-autocomplete library is added as a resource to the JSFiddle.

The allowNonVerifiedHouseNumber and allowNonVerifiedStreet indicate whether non-verified house numbers and streets should be included in the search results.

When a user chooses a location, our map will show the corresponding address and let you know if it needs to be verified:

autocompleteInput.on('select', (location) => {
  selectLocation(location);
});


function selectLocation(location) {
  cleanUp();

  selectedLocation = location;

  if (location) {
    locationNeedToBeClarified = (location.properties.nonVerifiedParts && location.properties.nonVerifiedParts.length) || !location.properties.housenumber;
  }

  zoomMap();
  showLocationMarker();
  updateAddressInfo();
}

If the address needs to be clarified, we add a dragEnd event handler to the map marker and offer the user a new location or address:

function showLocationMarker() {
  ...

  if (locationNeedToBeClarified) {
    locationMarker.on('dragend', (event) => {
      getAddressByLatLon(locationMarker.getLatLng().lat, locationMarker.getLatLng().lng).then(address => {
        suggectedLocation = address;
        updateAddressInfo();
      });
    })
  }
}

Address form 2: Structured address from a postcode to house number

The second address filling form shows how to collect structured address details, starting by entering the postcode:

  • When you select a postcode, the city name is pre-filled automatically;
  • It suggests cities, towns and villages that correspond to the provided postcode, as well as streets within the selected city;
  • It shows how to verify the entered address.

Adding autofill inputs for postcode, city, and street:

const postcodeInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("postcode"),
  myAPIKey, { lang: lang, type: "postcode", skipDetails: true, placeholder: "XXXXX", skipIcons: true, filter: { countrycode: [country] } });

const cityInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("city"),
  myAPIKey, { lang: lang, type: "city", skipDetails: true, skipIcons: true, placeholder: " ", filter: { countrycode: [country] } });

const streetInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("street"),
  myAPIKey, { lang: lang, type: "street", skipDetails: true, skipIcons: true, placeholder: " ", filter: { countrycode: [country] } });

Each of the autocomplete functions has an event listener for the ‘select’ event, which is triggered when a location is selected from the search results. When this event is triggered, the select function for the corresponding autocomplete function is called with the selected location as an argument:

postcodeInput.on('select', (postcode) => {
  postcodeData = postcode;

  // When you select a specific postcode, the input can be filled out automatically for many countries.
  if (postcode && !cityData && postcode.properties.city) {
    cityInput.setValue(postcode.properties.city);
  }

  updateFilters();
});


cityInput.on('select', (city) => {
  cityData = city;

  if (city) {
    cityInput.setValue(city.properties.city || '');
  }

  if (city && city.properties.postcode) {
    postcodeInput.setValue(city.properties.postcode);
  }
 
  updateFilters();
});

streetInput.on('select', (street) => {
  if (street) {
    streetInput.setValue(street.properties.street);
  }
  
  if (street && !postcodeData && street.properties.postcode) {
  	postcodeInput.setValue(street.properties.postcode);
  }
  
  if (street && !cityData && street.properties.city) {
  	cityInput.setValue(street.properties.city);
  }
});

The postcodeInput autocomplete function has an additional setPostprocessHook function. This function takes in a callback function as an argument, which is called with the selected location as an argument and returns the postcode of the location, hence allowing the dropdown of search results to only show postcodes:

postcodeInput.setPostprocessHook((feature) => {
  // show only postcode in the dropdown
  return feature.properties.postcode;
});

The updateFilters function is called whenever a location is selected from one of the autocomplete functions. This function updates the filters for the cityInput and streetInput autocomplete functions based on the selected postcode or city:

function updateFilters() {
  // update city filters
  if (postcodeData) {
    cityInput.addFilterByPlace(postcodeData.properties.place_id);
  } else {
    // set original filters
    cityInput.clearFilters();
    cityInput.addFilterByCountry([country]);
  }

  // update street filters
  if (postcodeData) {
    streetInput.addFilterByPlace(postcodeData.properties.place_id);
  } else if (cityData) {
    streetInput.addFilterByPlace(cityData.properties.place_id);
  } else {
    // set original filters
    streetInput.clearFilters();
    streetInput.addFilterByCountry([country]);
  }
}

The checkAddress function verifies the address. You can check if the provided address can be found in address databases:

function checkAddress() {
	const postcode= postcodeInput.getValue();
  const city = cityInput.getValue();
  const street = streetInput.getValue();
  const housenumber = document.getElementById("housenumber").value;
  
  const message = document.getElementById("message");
  message.textContent = "";
  
  if (!postcode || !city || !street || !housenumber) {
  	highlightEmpty();
    message.textContent = "Please fill in the required fields and check your address again.";
    return;
  }
  
  // Check the address with Geoapify Geocoding API
  // You may use it for internal information only. Please note that house numbers might be missing for new buildings and non-mapped buildings. So consider that most addresses with verified streets and cities are correct.
  fetch(`https://api.geoapify.com/v1/geocode/search?housenumber=${encodeURIComponent(housenumber)}&street=${encodeURIComponent(street)}&postcode=${encodeURIComponent(postcode)}&city=${encodeURIComponent(city)}&filter=countrycode:de&lang=${lang}&apiKey=${myAPIKey}`).then(result => result.json()).then((result) => {
  	let features = result.features || [];
    
    // To find a confidence level that works for you, try experimenting with different levels
    const confidenceLevelToAccept = 0.25;
  	features = features.filter(feature => feature.properties.rank.confidence >= confidenceLevelToAccept); 
  
  	if (features.length) {
    		const foundAddress = features[0];
    		if (foundAddress.properties.rank.confidence === 1) {
        	message.textContent = `We verified the address you entered. The formatted address is: ${foundAddress.properties.formatted}`;
        } else if (foundAddress.properties.rank.confidence > 0.5 && foundAddress.properties.rank.confidence_street_level === 1) {
        	message.textContent = `We have some doubts about the accuracy of the address: ${foundAddress.properties.formatted}`
        } else if (foundAddress.properties.rank.confidence_street_level === 1) { 
        	message.textContent = `We can confirm the address up to street level: ${foundAddress.properties.formatted}`
        } else {
        	message.textContent = `We can only verify your address partially. The address we found is ${foundAddress.properties.formatted}`
        }
    } else {
    		message.textContent = "We cannot find your address. Please check if you provided the correct address."
    }
  });
}

Additionally, this form allows you to set country and language by using two-letter codes, ISO 3166-1 alpha-2 and ISO 639-1 codes respectively.

Address form 3: Filling the address form starting with the street

The third address form sample shows how to fill in addresses starting with the street name:

  • you can search the world's addresses or narrow your search to a specific country;
  • you can fill in the full address in one field, and all the other fields will be filled automatically;
  • you can double-check the address you entered to make sure it's correct.

Similar to the previous example, we add address fields:

const streetInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("street"),
  myAPIKey, { allowNonVerifiedHouseNumber: true, allowNonVerifiedStreet: true, skipDetails: true, skipIcons: true, placeholder: " " });

const stateInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("state"),
  myAPIKey, { type: "state", skipDetails: true, placeholder: " ", skipIcons: true });

const cityInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("city"),
  myAPIKey, { type: "city", skipDetails: true, skipIcons: true, placeholder: " " });

const countryInput = new autocomplete.GeocoderAutocomplete(
  document.getElementById("country"),
  myAPIKey, { type: "country", skipDetails: true, placeholder: " ", skipIcons: true });

Each of the autocomplete functions has an event listener for the `select`` event, which is triggered when a location is selected from the search results. When this event is triggered, the select function for the corresponding autocomplete function is called with the selected location as an argument:

streetInput.on('select', (street) => {
  if (street) {
    streetInput.setValue(street.properties.street || '');
  }

  if (street && street.properties.housenumber) {
    housenumberElement.value = street.properties.housenumber;
  }

  if (street && street.properties.postcode) {
    postcodeElement.value = street.properties.postcode;
  }

  if (street && street.properties.city) {
    cityInput.setValue(street.properties.city);
  }

  if (street && street.properties.state) {
    stateInput.setValue(street.properties.state);
  }

  if (street && street.properties.country) {
    countryInput.setValue(street.properties.country);
  }
});

cityInput.on('select', (city) => {

  if (city) {
    cityInput.setValue(city.properties.city || '');
  }

  if (city && city.properties.postcode) {
    postcodeElement.value = city.properties.postcode;
  }

  if (city && city.properties.state) {
    stateInput.setValue(city.properties.state);
  }

  if (city && city.properties.country) {
    countryInput.setValue(city.properties.country);
  }
});

stateInput.on('select', (state) => {

  if (state) {
    stateInput.setValue(state.properties.state || '');
  }

  if (state && state.properties.country) {
    countryInput.setValue(state.properties.country);
  }
});

The select functions for the input field autocomplete functions all update the values of the corresponding input fields and may also update the values of other input fields based on the selected location.

The checkAddress function is used to verify the entered address and returns a result indicating whether or not it was able to verify it. If the address was verified partially (that is, it matches an address in a different state within the country), it would provide the address that matches that address.

Conclusion

We've provided you with examples of address-filling forms that you can use in your projects and applications. The provided code is flexible, so you can change its appearance and behavior as well as provide custom labels and messages.

You can use it to create almost any address collection form that fits your needs. For more information, check Geoapify's Geocoder Autocomplete tutorial.

What's next