iegik
10/5/2017 - 2:29 PM

Place Predictions Interface

Place Predictions Interface

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
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;
        }, {});
};
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);
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;
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;
        }, {});
};
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
    });
};