Клуб API Карт

Кластер. AJAX. Смена balloonContentBody при выборе объекта из списка.

Пост в архиве.
KDV
10 июля 2012, 16:41

Имеется массив PM[] = new ymaps.Placemark(...)

Так сложилось, что у нескольких меток в нем одинаковые координаты и при клике по данному кластеру открывается не увеличенная карта с раздельными метками, а табличка, в которой слева перечислены названия элементов clusterCaption, вошедших в данный кластер, а справа должно отображаться содержимое балуна при выборе одного из элементов. Для каждой метки определено событие click, которое через AJAX загружает содержимое балуна balloonContentBody, но в данном случае оно не отрабытывается. Как исправить данную ситуацию и всеже перехватить событие выбора элемента в кластере для подгрузки balloonContentBody через AJAX ? 

 

 

var cluster = new ymaps.Clusterer({gridSize:100});
 
function AddMarker(id, lat, lng, icon, hint) { 
PM[id] = new ymaps.Placemark([lat/1e6, lng/1e6], { 
 id:id, iconContent:icon, clusterCaption:icon, hintContent:hint, balloonContentBody:""});
PM[id].events.add('click', function (pEvent) { GetPMInfo(pEvent.get('target')); });
cluster.add(PMS[id]); } 

function GetPMInfo(PM) {
pmid = PM.properties.get('id');
AJAX("GET", "", "id="+pmid, ShowPMInfo); }

function ShowPMInfo() {
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete") {
PMS[pmid].properties.set('balloonContentBody', xmlHttp.responseText);
PMS[pmid].balloon.open(); }}

map.geoObjects.add(cluster);

  

31 комментарий

Я сделал это приблизительно так: навесил обработчик на клик по кластеру, в котором смотрю, достигнут ли максимальный масштаб в этой области карты, или нет (для того, чтобы показывать балун только когда дальнейшее увеличение карты по клику на кластер не возможно), если масштаб максимальный, то запрашиваю данные с сервера и вывожу их в содержимом балуна. 

Примерный код: (навыдергивал кусочков, но суть такая)

 

clusterer.createCluster = function (center, geoObjects) {

                var cluster = ymaps.Clusterer.prototype.createCluster.call(this, center, geoObjects);


                cluster.properties.set('cluster', cluster);

                cluster.options.set('openBalloonOnClick', false);

                cluster.events.add('click', function(e){

                     if (map.getZoom() == map.zoomRange.getCurrent()[1]) {

                        $.getJSON('/').success(function(data){

                            cluster.properties.set({balloonContent:clusterBalloonContent});

                        })

                     }

                    return cluster;

                });

                return cluster;

            }

 

т.е. другими словами в API 2 не предумотрено каких либо штатных событий по смене фокуса внутри кластера? Так, чтоб без экзорцизма с проверкой масштаба и т.п., т.к. потенциально баг в этом месте может быть (например кластер состоит из нескольких точек с одинаковыми координатами на всех масштабах карты).

За пример спасибо, а можно без jQuery ? И в примере невидно в каком месте идет подгрузка содержимого с AJAX.

Ну, эту ситуацию же можно тоже разрешить - есть доступ к точкам, состаляющим кластер. Нужно открыть балун раньше - пожалуйста. Мне просто надо было именно тогда, когда уже кластер точно не раскрывается.

Так в примере от jQuery как раз и есть только вызов  $.getJSON для загрузки данных с сервера. Можете использовать что угодно тут. 

с Вашей помощью набросал следующее:

cluster = new ymaps.Clusterer({gridSize:50, minClusterSize:2});
    cluster.createCluster = function(center, geoObjects) {
     var cluster = ymaps.Clusterer.prototype.createCluster.call(this, center, geoObjects);
     cluster.events.add('click', function(e) {
        if(map.getZoom() < map.zoomRange.getCurrent()[1]) return cluster;
       var objs = cluster.properties.get('geoObjects');
       for(i=0; i       });
     return cluster; };

 Судя по логам вебсервера все происходит именно так как и нужно, т.е. при клике по кластеру при максимальном зуме карты над ним происходит N запросов AJAX где N = кол-ву меток в кластере. Разумеется balloonContent заполняется асинхронно для всех меток, но на балун кластера повлиять уже не успевает, в итоге в нем отображается нормально только последняя метка в списке. Есть макс.дешевый способ обновить этот балун после отработки всех N ajax запросов ?

 

 

 

 Судя по логам вебсервера все происходит именно так как и нужно, т.е. при клике по кластеру при максимальном зуме карты над ним происходит N запросов AJAX где N = кол-ву меток в кластере

 

Жесть. Зачем N запросов?

получайте одним запросом с сервера список геообъектов в одном кластере, сформируйте и установите новый контент балуна кластера в обработчике AJAX запроса один раз

Не все так банально как кажется)

В случае обычных placemark по событию click заполняется через AJAX их balloonContentBody(если это уже не было сделано ранее) и все отлично.

Но вот в случае попадания метки в кластер событие click для  placemark перестает отрабатываться, хотя было бы логично, если API его генерировало при смене выбранного элемента в списке(на скриншоте). Но коли это нереализуемо, то приходится извращаться, в часности запросить список ID меток элементов в кластере и для каждой из них заполнить свой balloonContentBody. 

Насчет балуна для всего кластера - он должен отображаться как на скрине, т.е. с возможностью выбора конкретного пункта в его составе, а не просто заполненный текстом статичный балун как уже видел примеры кастомизации кластерных балунов здесь в клубе, прошу Вас описать детальнее, желательно в коде))

 

Но коли это нереализуемо, то приходится извращаться, в часности запросить список ID меток элементов в кластере и для каждой из них заполнить свой balloonContentBody. 

не вижу чему это противоречит.

Я лишь писал о том что эти данные можно и лучше получять одним запросом.

 

Насчет балуна для всего кластера - он должен отображаться как на скрине, т.е. с возможностью выбора конкретного пункта в его составе, а не просто заполненный текстом статичный балун как уже видел примеры кастомизации кластерных балунов здесь в клубе, прошу Вас описать детальнее, желательно в коде))

заполненный статичным текстом или интерактивный вы решаете сами когда создаете ваш собственный макет балуна

"не вижу чему это противоречит."
Так как метки распределяются по кластерам динамически, то вполне ожидаемо чтобы событие click по ним отрабатывалось независимо от того в кластере они или нет.

Пример в студию плиз по примеру заполнения интерактивного балуна кластера!
Ни в примерах к API ни в примерах в клубе такого не нашел, в лучшем случае статика.
Для истории отмечу: проблему я решил последовательным заполнением через AJAX свойства balloonContentBody у всех меток в составе кластера, переопределять стандартный интерактивный балун кластера даже не пришлось. После запроса всех необходимых данных открываю балун для кластера и все норм.
PS У кого похожая проблема - пишите, опишу подробнее.

Уже готовятся к выходу улучшения кластеров в этом месте. Например можно будет легко отслеживать активный объект балуна. И добавлю автоперестроение балуна кластера при обновлении данных метки.

это радует, будем ждать.

Вы плохо искали.

Сделал самый простой пример.

А вообще постигать что-то по примерам - всё равно что смотреть телевизор - ведет к отупению. Из доки ясно что кластер - обычный геообъект и балун у него такой же обычный. Точки кластера получаем из данных, берем из них id и идем за контентом, всё.

Видел этот пример! Много раз уже повторялся: мне НЕ нужен статичный балун! Надо интерактивный! Ради статичного я бы и вопрос задавать тут не стал.
PS ТВ у меня нет уже более года, так что проблема не актуальна)))

там список в балуне строится по данным объектов из кластера.

вы можете получить данные этих меток,

или сходить за этими данными AJAX-ом и построить нужный html, навесить на него события...

Саму суть как так получается я понимаю, но я тут задавал вопрос - как РЕШИТЬ эту задачку, а не общие принципы работы с AJAX и событиями. Другими словами больше кода - меньше теории.

не понятно что именно не получается решить?

 

как добраться до меток из клатера - понятно (через cluster.properties.get('geoObjects'))

Из этого списка меток можно получить их идентификаторы для ajax-запроса,

получив ответ - перестроить контент балуна

Все шаги решения описаны, а что именно тогда вызывает затруднения? 

Перестроить контент ИНТЕРАКТИВНОГО балуна нужен пример в студию! (уже раз в 3й прошу а все никак)
PS И не надо снова затирать про geoObjects, сам выше уже давно написал, что через них и сделал список ID.

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

У меня при properties.set('myproperty', 'asdasd'); ВСЕГДА происходил build - т.е. перерисовка, потому что я менял проперти балуна при его создании.

Меня это наоборот смущало, потому что я не понимаю, зачем дважды рисовать, и не догадался, как при обращении ЗАРАНЕЕ (до build) всунуть туда нужную мне информацию.

Все это работает на listener'ах и врядли как-то может сломаться. Разве что из-за ошибки в коде.

Так я и думал. На словах все элементарно, а как доходит до демонстрации реального примера - все глухо. Печалька.

в чем должна заключаться "интерактивность" балуна?

Снова "приплыли", расписывал-расписывал выше по тексту и на те...
Ситуация такова: несколько placemark с ОДИНАКОВЫМИ координатами в одном кластере, обратите внимание на балун кластера по умолчанию, вот именно ТАКАЯ интерактивность и нужна! С той лишь разницей, что балун должен заполняться динамически через AJAX и я буду только рад, если это возможно реализовать за 1 запрос.

А еще мне категорически не понятно почему при открытии балуна у точки иконка пропадает, а при открытии балуна у кластера - нет. И никто мне не отвечает, как это исправить. =(

=)

поставьте у плейсмарка опцию hideIconOnBalloonOpen: false

Мне как раз обратное нужно =)

У кластера опция hideIconOnBalloonOpen: true =)


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

Марина, как здорово что вы вернулись =) Я, правда, уже со всем, кроме исчезновения метки разобрался. 

Опция нужна исключительно для создания однообразия поведения объектов - а то как то странно получается что метка пропадает, а кластер нет.

пока не добавили опцию в кластеры - можно повесить 2 события на балун кластера (опен и клос), где и скрывать элемент. грубо говоря: cluster.balloon.events.add('open', function(e){ $(cluster.balloon...getElement()).hide() }), и .show() соотв на close.

А как мне из контекста балуна добраться до метки? 

Ну в смысле что скрывается за многоточием вот здесь: $(cluster.balloon...getElement()).hide() 

увы ;-( это фиш, который, надеюсь, стоит где-то в планах.

я решал сохранением ссылки на открытый кластер при его открытии. но у меня там весь балун кастомный, и много всякого окружения.

можно примерно так попробовать:

(function(){

var ballon;

cluster.events.add('balloonopen', function (e){

  balloon = e.get('target');

  console.debug(e.get('geoObjects')); // если правильно помню. в этом контейнере евента много ссылок на разные данные

});

// дальше в коде

...

  ..., function () {

    if (!ballon.isOpen()) return;

    // делаем что-то

    console.info(balloon.properties.getAll());

  });

})(); // замыкаем, опционально. можно и без этого

Вероятно я обманул с названиями событий, надо в доках/исходниках уточнить.

Но основная мысль - заранее схоронить гденибудь ссылку на балун рядом с кластером.

Можно переопределить clusterer.createCluster, чтобы сохранить ссылку на cluster где-нибудь или повесить на него нужные события. Т.е. сохраняем ссылку и пробрасываем выполнение дальше, например, так:

clusterer.origCreateCluster = clusterer.origCreateCluster;

clusterer.createCluster = function () {

  // делаем что-то

  this.origCreateCluster.apply(this, arguments);

  this.events.fire('createcluster', {target: cluster}); // Я таким образом на скорую руку инициировал свое событие

  // еще что-то делаем

}

И уже совсем в другом месте его ловил (это событие) и навешивал нужные мне при конкретной реализации действия. В вашем случае можете не усложнять и сохранить ссылку как-нибудь так:

var cluster;

...

clusterer.createCluster ... function(){

  this.origCreateCluster.apply(this, arguments);

  cluster = this;

};

и иметь балун для конкретного кластера через cluster.balloon

Вариантов достаточно много, на самом деле. Я не знаю, что у вас уже есть, поэтому сложно с потолка предложить решение.