Динамическая загрузка содержимого балуна

Open in CodeSandbox

Часто при работе с картой приходится загружать большой объем дополнительных данных (текстовая информация, картинки и пр.). При этом загруженная информация не всегда используется пользователем. Например, когда на карте отображены сотни меток, и балун каждой из них содержит большой объем текста. При работе с картой пользователь может не открыть ни один балун, но данные все равно будут загружены.

В таких случаях в целях экономии трафика имеет смысл загружать данные не сразу, а по мере необходимости.

Данный пример демонстрирует динамическую загрузку содержимого балуна с помощью AJAX-запроса.

При клике на значок кластера или одиночной метки производится проверка, загружено ли содержимое их балуна. Если для каких-либо меток текст балуна не задан, серверу отправляется POST-запрос с массивом идентификаторов этих меток. Сервер обрабатывает массив и на его основе возвращает JSON-объект, содержащий необходимые данные.

Обратите внимание, что серверную часть необходимо реализовать самостоятельно.

<!DOCTYPE html>

<html>
    <head>
        <title>Динамическая загрузка содержимого балуна</title>
        <meta
            http-equiv="Content-Type"
            content="text/html; charset=utf-8"
        />
        <!--
        Укажите свой API-ключ. Тестовый ключ НЕ БУДЕТ работать на других сайтах.
        Получить ключ можно в Кабинете разработчика: https://developer.tech.yandex.ru/keys/
    -->
        <script
            src="https://api-maps.yandex.ru/2.1/?lang=ru_RU&amp;apikey=<ваш API-ключ>"
            type="text/javascript"
        ></script>
        <script
            src="https://yandex.st/jquery/2.2.3/jquery.min.js"
            type="text/javascript"
        ></script>
        <script src="om_balloon_ajax.js" type="text/javascript"></script>
        <style>
            html,
            body,
            #map {
                width: 100%;
                height: 100%;
                padding: 0;
                margin: 0;
            }
        </style>
    </head>

    <body>
        <div id="map"></div>
    </body>
</html>
ymaps.ready(function () {
    var myMap = new ymaps.Map("map", {
            center: [55.751574, 37.573856],
            zoom: 10,
            controls: [],
        }),
        objectManager = new ymaps.ObjectManager({
            clusterize: true,
            clusterDisableClickZoom: true,
        });
    myMap.geoObjects.add(objectManager);

    objectManager.objects.events.add("balloonopen", function (e) {
        // Получим объект, на котором открылся балун.
        var id = e.get("objectId"),
            geoObject = objectManager.objects.getById(id);
        // Загрузим данные для объекта при необходимости.
        downloadContent([geoObject], id);
    });

    objectManager.clusters.events.add("balloonopen", function (e) {
        // Получим id кластера, на котором открылся балун.
        var id = e.get("objectId"),
            // Получим геообъекты внутри кластера.
            cluster = objectManager.clusters.getById(id),
            geoObjects = cluster.properties.geoObjects;

        // Загрузим данные для объектов при необходимости.
        downloadContent(geoObjects, id, true);
    });

    function downloadContent(geoObjects, id, isCluster) {
        // Создадим массив меток, для которых данные ещё не загружены.
        var array = geoObjects.filter(function (geoObject) {
                return (
                    geoObject.properties.balloonContent ===
                        "идет загрузка..." ||
                    geoObject.properties.balloonContent === "Not found"
                );
            }),
            // Формируем массив идентификаторов, который будет передан серверу.
            ids = array.map(function (geoObject) {
                return geoObject.id;
            });
        if (ids.length) {
            // Запрос к серверу.
            // Сервер обработает массив идентификаторов и на его основе
            // вернет JSON-объект, содержащий текст балуна для
            // заданных меток.
            ymaps.vow
                .resolve(
                    $.ajax({
                        // Обратите внимание, что серверную часть необходимо реализовать самостоятельно.
                        //contentType: 'application/json',
                        //type: 'POST',
                        //data: JSON.stringify(ids),
                        url: "content.json",
                        dataType: "json",
                        processData: false,
                    })
                )
                .then(function (data) {
                    // Имитируем задержку от сервера.
                    return ymaps.vow.delay(data, 1000);
                })
                .then(
                    function (data) {
                        geoObjects.forEach(function (geoObject) {
                            // Содержимое балуна берем из данных, полученных от сервера.
                            // Сервер возвращает массив объектов вида:
                            // [ {"balloonContent": "Содержимое балуна"}, ...]
                            geoObject.properties.balloonContent =
                                data[geoObject.id].balloonContent;
                        });
                        // Оповещаем балун, что нужно применить новые данные.
                        setNewData();
                    },
                    function () {
                        geoObjects.forEach(function (geoObject) {
                            geoObject.properties.balloonContent =
                                "Not found";
                        });
                        // Оповещаем балун, что нужно применить новые данные.
                        setNewData();
                    }
                );
        }

        function setNewData() {
            if (isCluster && objectManager.clusters.balloon.isOpen(id)) {
                objectManager.clusters.balloon.setData(
                    objectManager.clusters.balloon.getData()
                );
            } else if (objectManager.objects.balloon.isOpen(id)) {
                objectManager.objects.balloon.setData(
                    objectManager.objects.balloon.getData()
                );
            }
        }
    }

    $.ajax({
        url: "data.json",
    }).done(function (data) {
        objectManager.add(data);
    });
});