Макет балуна кластера

Open in CodeSandbox

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

Макеты объектов можно создавать с помощью фабрики templateLayoutFactory, используя текcтовые шаблоны. Текстовые шаблоны формируют html-содержимое макета на основе хэша с данными, передаваемого в конструктор макета. В текстовых шаблонах можно использовать названия полей первого и второго уровня вложенности переданного хэша. При необходимости использования значений полей третьего и более уровней вложенности можно создавать внутренний подмакет, в который будет передаваться модифицированный хэш данных.

Балун кластера по умолчанию отслеживает клики на элементах списка и записывает выбранный объект в поле кластера cluster.state.get('activeObject'). Макет правой части балуна может через значение этого поля получить ссылку на текущий активный геообъект и отобразить его данные, а также отслеживать изменение активного объекта и обновлять содержимое правой части.

<!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.0/?load=package.standard,package.clusters&amp;lang=ru-RU&amp;apikey=<ваш API-ключ>"
            type="text/javascript"
        ></script>
        <style>
            .cluster-balloon-item {
                margin: 10px;
                cursor: pointer;
            }

            #map {
                width: 400px;
                height: 300px;
            }
        </style>

        <script
            src="cluster_balloon_layout.js"
            type="text/javascript"
        ></script>
    </head>

    <body>
        <div id="map"></div>
    </body>
</html>
ymaps.ready(init);

function init() {
    var center = [55.74954, 37.621587],
        myMap = new ymaps.Map("map", {
            center: center,
            zoom: 10,
        }),
        // Создадим макет правой части балуна кластера.
        MainContentLayout = ymaps.templateLayoutFactory.createClass("", {
            build: function () {
                // Сначала вызываем метод build родительского класса.
                MainContentLayout.superclass.build.call(this);
                // Нужно отслеживать, какой из пунктов левого меню выбран,
                // чтобы обновлять содержимое правой части.
                this.stateListener = this.getData()
                    .state.events.group()
                    .add("change", this.onStateChange, this);
                // Запоминаем текущий активный объект.
                this.activeObject =
                    this.getData().state.get("activeObject");
                this.applyContent();
            },

            clear: function () {
                if (this.activeObjectLayout) {
                    this.activeObjectLayout.setParentElement(null);
                    this.activeObjectLayout = null;
                }
                // Снимаем слушателей изменения полей.
                this.stateListener.removeAll();
                // А затем вызываем метод clear родительского класса.
                MainContentLayout.superclass.clear.call(this);
            },

            onStateChange: function () {
                // При изменении одного из полей состояния
                // проверяем, не сменился ли активный объект.
                var newActiveObject =
                    this.getData().state.get("activeObject");
                if (newActiveObject != this.activeObject) {
                    // Если объект изменился, нужно обновить
                    // содержимое правой части.
                    this.activeObject = newActiveObject;
                    this.applyContent();
                }
            },

            applyContent: function () {
                if (this.activeObjectLayout) {
                    this.activeObjectLayout.setParentElement(null);
                }
                // Чтобы было удобнее формировать текстовый шаблон,
                // создадим внутренний макет, в который будем передавать
                // модифицированный dataSet.

                this.activeObjectLayout = new MainContentSubLayout({
                    // Поскольку внутренний макет будет отображать
                    // информацию какого-то геообъекта,
                    // будем передавать во входном хэше данные и опции
                    // текущего активного геообъекта.
                    options: this.options,
                    properties: this.activeObject.properties,
                });

                // Прикрепляем внутренний макет к внешнему.
                this.activeObjectLayout.setParentElement(
                    this.getParentElement()
                );
            },
        }),
        // Внутрений подмакет правой части балуна кластера.
        MainContentSubLayout = ymaps.templateLayoutFactory.createClass(
            // Мы можем использовать поля properties геообъекта,
            // так как будем передавать properties в конструктор макета.
            "<h3>$[properties.name]</h3>" +
                '<div width="100">' +
                "$[properties.balloonContentHeader]<br>" +
                "$[properties.balloonContentBody]" +
                "</div>"
        ),
        // Создадим макет для элемента списка в левой части балуна.
        ItemLayout = ymaps.templateLayoutFactory.createClass(
            '<div class="cluster-balloon-item" [if data.isSelected]style="font-weight: bold;"[endif]>$[properties.name]</div>'
        ),
        // Создадим кластеризатор и выставим ему созданные макеты
        // через опции.
        clusterer = new ymaps.Clusterer({
            // Поскольку опции задаются для кластеров, а не для всего
            // кластеризатора, им нужно приписать префикс 'cluster'.
            clusterDisableClickZoom: true,

            // Если нужно задать опции для балуна кластера, то к названию
            // опции приписываются сразу 2 префикса - 'cluster' и 'balloon'.
            clusterBalloonMainContentLayout: MainContentLayout,
            clusterBalloonSidebarItemLayout: ItemLayout,
            // Настроим ширину левой части балуна кластера
            clusterBalloonSidebarWidth: 100,
            // и ширину балуна целиком.
            clusterBalloonWidth: 300,
        }),
        geoObjects = [];

    // Создадим 500 меток со случайными координатами около центра Москвы.
    for (var i = 0; i < 500; i++) {
        var coordinates = [
            center[0] +
                0.5 * Math.random() * (Math.random() < 0.5 ? -1 : 1),
            center[1] +
                0.7 * Math.random() * (Math.random() < 0.5 ? -1 : 1),
        ];
        geoObjects[i] = new ymaps.Placemark(coordinates, {
            name: "Метка №" + i,
            clusterCaption: "Метка №" + i,
            balloonContentBody:
                "<br>Варкалось. Хливкие шорьки<br>" +
                "Пырялись по наве<br>" +
                "И хрюкотали зелюки,<br>" +
                "Как мюмзики в мове.<br>",
            balloonContentHeader: "Бармаглот",
            balloonContentFooter: "Л. Кэрролл",
        });
    }

    // Добавим полученные геообъекты в кластеризатор.
    clusterer.add(geoObjects);
    // А сам кластеризатор добавим на карту.
    myMap.geoObjects.add(clusterer);
}