Skip to content

Instantly share code, notes, and snippets.

@iegik
Last active June 11, 2018 21:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iegik/a23e38a997706b53e54f16e0d6caf9f4 to your computer and use it in GitHub Desktop.
Save iegik/a23e38a997706b53e54f16e0d6caf9f4 to your computer and use it in GitHub Desktop.
Place Predictions Interface
const DDATA_API_KEY = 'YOUR_API_KEY';
const ddataAPI = body => {
return new Promise((response, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("POST", "https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address?5");
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Authorization", `Token ${DDATA_API_KEY}`);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
try {
response(JSON.parse(xhr.responseText));
} catch (e) {
reject({errors: [e]})
}
}
};
xhr.send(JSON.stringify(body));
});
};
/**
* Получение совпадений адресов по тексту
* @param {string} query
* @returns {Promise.<*>}
* */
export const getSuggestions = (query) => ddataAPI({
query,
count: 10
});
/**
* Получение совпадений российских городов по тексту
* @param {string} query
* @returns {Promise.<*>}
* */
export const getCitySuggestions = (query) => ddataAPI({
restrict_value: false,
from_bound: {value: "city"},
to_bound: {value: "settlement"},
query,
count: 10
});
/**
* Получение совпадений российских городов по тексту
* @param {string} data
* @param {string} query
* @returns {Promise.<*>}
* */
export const getStreetSuggestions = (data, query) => {
let {city_id, region_id} = data;
return ddataAPI({
locations: [
{
city_fias_id: city_id,
region_fias_id: region_id,
}
],
from_bound: {value: "street"},
to_bound: {"value": "street"},
query,
"count": 10
});
};
export const mapDDataPredictions = place => {
if(!place) return;
return place && reduce(place, (frmData, value, key) => {
let addressType = key;
if (addressType === 'postal_code') frmData['zipcode'] = value;
if (addressType === 'flat') frmData['flat'] = value;
if (addressType === 'street_with_type') frmData['street'] = value;
if (addressType === 'house') frmData['house'] = value;
if (addressType === 'city_with_type') frmData['city'] = value;
if (addressType === 'region_with_type') frmData['city'] = !isEmpty(frmData['city']) ? (frmData['city'] + ", " + value) : value;
if (addressType === 'country') {
frmData['countryName'] = 'Россия';
frmData['country'] = 'RU';
}
return frmData;
}, {});
};
import React from 'react';
import {Select, List, ListItem, TextInput} from 'react-autocomplete-field';
import {getStreetSuggestions} from './ddata-api';
import {createAutocompleteService} from './google-api';
class StreetAutocomplete extends Select {
renderInput = TextInput;
renderAutocomplete = List;
renderAutocompleteItem = ListItem;
search = text => {
if (this.props.data.country === 'RU') {
return getStreetSuggestions(this.props.data, text).then(({suggestions}) => suggestions);
}
return createAutocompleteService(this.props.data.country, this.props.data.city, null, text);
};
getSuggestionText = item => {
if (this.props.data.country === 'RU') {
return item.data.street_with_type;
}
return item.structured_formatting.main_text;
};
getSuggestionValue = item => {
if (this.props.data.country === 'RU') {
return {
value: item.unrestricted_value,
text: item.data.street_with_type,
settlement: item.data.settlement,
city_id: item.data.city_fias_id,
region_id: item.data.region_fias_id
};
}
return {
value: item.structured_formatting.main_text,
text: item.structured_formatting.main_text,
settlement: null,
place_id: item.place_id,
};
}
}
export default StreetAutocomplete;
import {isEmpty, reduce} from 'lodash';
const parseRestrictions = (types, values, input) => ({
types, // address geocode
componentRestrictions: values,
input
});
const searchBy = (country, city, address, input) => {
let full_address = {};
let types;
if (country) {
types = ['(cities)'];
full_address.country = country.toLowerCase();
}
if (city) {
types = ['address'];
if (!isEmpty(input)) {
input = city + ', ' + input;
}
}
if (address) {
types = ['geocode'];
full_address.street_address = address;
}
return parseRestrictions(types, full_address, input)
};
const createGoogleAutocompleteFilter = ac => (country, city, address, input) => {
let options = searchBy(country, city, address, input);
ac.setComponentRestrictions(options.componentRestrictions);
ac.setTypes(options.types);
};
const createGoogleAutocomplete = (element, callback) => {
let autocomplete = new google.maps.places.Autocomplete(element);
autocomplete.addListener('place_changed', function() {
callback(this.getPlace().address_components);
});
return autocomplete;
};
const clearGoogleAutocomplete = (ac, element) => {
ac.unbindAll();
google.maps.event.clearInstanceListeners(element);
};
const createGoogleAutocompleteServiceFilter = (ac, cb) => (country, city, address, input) => {
let options = searchBy(country, city, address, input);
ac.getPlacePredictions(options, cb);
};
const createGoogleAutocompleteService = (country, city, address, input) => new Promise((callback) => {
let autocomplete = new google.maps.places.AutocompleteService();
const OK = google.maps.places.PlacesServiceStatus.OK;
return createGoogleAutocompleteServiceFilter(autocomplete, function (predictions, status) {
if (status !== OK) {
return;
}
callback(predictions)
})(country, city, address, input);
});
const call = fn => (...args) => typeof google !== 'undefined'
? fn(...args)
: Promise.reject('Google API is not ready jet');
export const createAutocomplete = call(createGoogleAutocomplete);
export const createAutocompleteFilter = call(createGoogleAutocompleteFilter);
export const createAutocompleteService = call(createGoogleAutocompleteService);
export const clearAutocomplete = call(clearGoogleAutocomplete);
export const mapPlacePredictions = place => {
if(!place) return;
return place && reduce(place, (frmData, address_component) => {
if (!address_component.types) return frmData;
let addressType = address_component.types[0];
if (addressType === 'postal_code') frmData['zipcode'] = address_component['short_name'];// long_name
if (addressType === 'street_number') frmData['flat'] = address_component['short_name'];
if (addressType === 'route') frmData['street'] = address_component['short_name'];
if (addressType === 'locality') frmData['city'] = address_component['long_name'];
if (addressType === 'administrative_area_level_1') frmData['city'] = (frmData['city']) ? frmData['city'] + ", " + address_component['short_name'] : address_component['short_name'];
if (addressType === 'country') {
frmData['countryName'] = address_component['long_name'];
frmData['country'] = address_component['short_name'];
}
return frmData;
}, {});
};

Google Place API

  1. Add following script:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initService"
        async defer></script>
  1. Fill YOUR_API_KEY
  2. Call createAutocompleteService(country?, city?, street?, text); as Promise

DData API

  1. Fill DDATA_API_KEY in ddata-api.js
  2. Call getCitySuggestions(text) or getStreetSuggestions({city_id, region_id}, text) as Promise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment