Клуб API Карт

Аяксовая подгрузка данных в кластер

Dimestel
4 декабря 2013, 16:51

Здравствуйте. Есть множество объектов, они добавлены в кластеры, кластеры на карту. У меток кастомный балун, причем пустой. По клику на метку получаем ид объекта и по нему подгружаем данные балун метки. Как сделать так, чтобы при клике на нераспадающийся кластер, в котором несколько объектов в одной точке, подгружались данные для каждого объекта по айдишникам объектов в кластере и на их основании уже отрисовывался бы балун кластера?

 

$(function() {
      var __map_points = new Array();
  //<web:repeater runat="server" datasourcemethod="GetMapProperties"><itemtemplate><web:placeholder runat="server" visibleproperty="HasMap">
    __map_points[__map_points.length] = {
    ID:<web:literal runat="server" property="Id" />,
    Longitude:<web:literal runat="server" property="LongitudeValue" />,
    Latitude:<web:literal runat="server" property="LatitudeValue" />
    }
  //</web:placeholder></itemtemplate></web:repeater>
      ymaps.ready(function() {
        if (__map_points.length > 0) {
          <%--создаем карту--%>
          var j_map_holder = $("#div_map_holder");

          map = new ymaps.Map(
            j_map_holder[0],
            {
              center: [__map_points[0].Latitude, __map_points[0].Longitude],
              zoom: 10
            }
          );
          
          map.controls.add("zoomControl");

          var myBalloonContentLayoutClass = ymaps.templateLayoutFactory.createClass(
            '<div class="ymaps_baloon_out">' +
              '<div class="baloon_title">$[properties.id]</div>'+
            '</div>'
          );
         
          
          // Создадим макет правой части балуна кластера.
          var MainContentLayout = ymaps.templateLayoutFactory.createClass('', {
            build: function() {
              // Сначала вызываем метод build родительского класса.
              var count = this.getData().cluster.properties._T.geoObjects.length;
              for (var i = 0; i < count; i++) {
                var id = this.getData().cluster.properties._T.geoObjects[i].properties._T.id;
                ajax_load_placemark(id, object, load_placemark_callback);
              }
              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());
            }
          });
          
          // Внутрений подмакет правой части балуна кластера.
          var MainContentSubLayout = ymaps.templateLayoutFactory.createClass(
            // Мы можем использовать поля properties геообъекта,
              // так как будем передавать properties в конструктор макета.
            '<h3>$[properties.id]</h3>' +
              '<div width="100">' +
              '$[properties.balloonContentBody]<br>' +
              '$[body]' +
              '</div>'
          );
        
          // Создадим макет для элемента списка в левой части балуна.
          ItemLayout = ymaps.templateLayoutFactory.createClass(
            '<div class="cluster-balloon-item" [if data.isSelected]style="font-weight: bold;"[endif]>$[properties.id]</div>'
          );
        
            
          var myClusterer = new ymaps.Clusterer({
            showInAlphabeticalOrder: true,
            clusterBalloonMainContentLayout: MainContentLayout,
            clusterBalloonSidebarItemLayout: ItemLayout,
            // Настроим ширину левой части балуна кластера
            clusterBalloonSidebarWidth: 100,
            // и ширину балуна целиком.
            clusterBalloonWidth: 300
          });

          

            <%--добавляем метки--%>
          for (var index = 0; index < __map_points.length; index++) {
            var pt = __map_points[index];
            var placemark = new ymaps.Placemark(
              [pt.Latitude, pt.Longitude],
              {
                id: pt.ID,
                body : "<div>111</div>"
              },
              { balloonContentLayout:myBalloonContentLayoutClass }
            );
            placemark.events.add('click', function(mark) {
              var object = mark.get('target');
              ajax_load_placemark(object.properties.get('id'), object, load_placemark_callback);
            });

          }

          function load_placemark_callback(data, object) {
            if (data) {
              var params = ".ymaps_baloon_out";
              var content = $(params).append(
                '<div class="ymaps_baloon">' +
                  '<div class="baloon_added"><span class="added">' + data.Added + '</span></div>' +
                  '<div class="baloon_title"><a href="' + data.Id + '">' + data.Title + '</a></div>' +
                  '<div class="baloon_image"><a href="' + data.Id + '"><img class="logoImg" src="' + data.ImgUrl + '"/></a></div>' +
                  '<div class="baloon_text">' +
                    '<div class="info">' + data.Type + ': <br/><b>' + data.Square + '</b><br/>Стоимость: <br/>' + data.Price + '</div>' +
                    '<div class="date">Добавлено: ' + data.District + '</div>' +
                  '</div>' +
                  '<div class="arrow_b"></div>' +
                  '<div class="clearfix"></div>' +
                  '</div>');
              object.properties.set('body', content);
            }
          }

          map.geoObjects.add(myClusterer);
          <%--если метка всего одна - то определяем, какой максимальный зум для ее координат--%>
          if (__map_points.length == 1) {
            var coords = [__map_points[0].Latitude, __map_points[0].Longitude];
            map.zoomRange.get(coords).then(function(range) {
              map.setCenter(coords, range[1], { callback: function() { bind_map_events(map); } });
            });
          } else {
            bind_map_events(map);
          }
          ymaps.util.bounds.getCenterAndZoom(map.geoObjects.getBounds(), [j_map_holder.width(), j_map_holder.height()]);
        } else {
          $("div.estate_list").addClass("hide_map");
        }
      });
    });

    function bind_map_events(map) {
      map.events.add('actionend', function() {
        var bounds = map.getBounds();
        page = 0;
        if (items_per_page == 0) items_per_page = 100;
        ajax_load_result(bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1], items_per_page, page * items_per_page, window.location.search.substring(1), load_properties_callback);
      });
    }

    function load_properties_callback(data) {
      if (data) {
        $("#changed_property").html($('#PropTmpl').tmpl(data));
        $("#changed_property_table").html($('#PropTmplTable').tmpl(data));
        if (data.length < items_per_page) {
          $("#ShowMoreButton").hide();
        } else {
          $("#ShowMoreButton").show();
        }
      }
    }

 

 

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

Посмотреть работу можно тут:
http://beta.zakvadrat.ru/search/?city=214

18 комментариев
Подписаться на комментарии к посту

Уберите, пожалуйста, код под кат (кнопка врезка)

 

var count = this.getData().cluster.properties._T.geoObjects.length;

Ни через какие педальные поля обращаться не надо, используйте только задокументированные интерфейсы:

var count = this.getData().cluster.properties.get('geoObjects').length;

Вы получили массив меток кластера, перебираете его, делаете запрос за данными, и когда они придут, строите html-содержимое балуна.

Рекомендую эти данные из запросов кешировать, иначе вы будуте при каждой смене масштаба запрашивать одно и тоже

Сделал как написали. Не помогло. Точнее чего-то не хватает, не пойму чего. Во-первых, отрабатывает почему-то только один коллбэк load_placemark_callback, хотя на сервере дважды метод вызывается. Но это уже другое дело. Даже для перевого объекта в списке ничего нет. Аяксово подгружается body для метки, оно же и забито в шаблон для правой части балуна кластера, но ничего нет(

Результат доступен по той же ссылке.

У вас там падает с ошибкой

Uncaught TypeError: Cannot read property '0' of null

Может координаты не корректные передаются.

Плюс не надо вешать обработчик события на каждую метку в цикле

повестьте один на кластеризатор

placemark.events.add('click', function(mark) {

              var object = mark.get('target');

              ajax_load_placemark(object.properties.get('id'), object, load_placemark_callback);

});

Ошибку исправил, посмотрите пожалуйста. Насчет один обработчик навесить на кластеризатор я не понял в чем идея, точнее не уверен что правильно понял.

вместо навешивания обработчиков в цикле на все метки, пишете:

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

    var geoObject = e.get('target'),

          isCluster = !!geoObject.properties.get('geoObjects');

     if(isCluster) {

          // click на кластере

      }

      else {

           // click на метке

       }

});

Выходит как будто бы хуже, т.к. это событие отрабатывает для всех меток из кластеризатора, т.е. для всех каждый раз, а в моем варианте только для тех что в нераспадающемся кластере, т.е. для 2 - 5,6. Но вообще самое главное-то - содержимое балуна кластера, которое строится из содержимого балуна меток пустое! :-(

Допомогите пожалуйста!

Я вам уже писал, чтобы клик срабатывал на кластере, нужно повесить один обработчик на кластеризатор, а не на каждую метку отдельно

Сделал как вы написали. Подгрузка не происходит(((

http://beta.zakvadrat.ru/search/?city=4&deal=1&group=-1&price=total&cur=rub&subtypes=None

То что я делаю работает не на главной странице, а только в результатах поиска.

myClusterer.events.add('click', function(e){
            var geoObject = e.get('target'),
            isCluster = !!geoObject.properties.get('geoObjects');
            if(isCluster) {
              if (map.getZoom() == map.zoomRange.getCurrent()[1]) {
                var object = e.get('target');
                if (object != undefined) {
                  var objects = object.properties.get('geoObjects');
                  for (var i = 0; i < objects.length; i++) {
                    ajax_load_placemark(objects[i].properties.get('id'), object, load_placemark_callback);
                  }
                }
              }
            }
          });

var params = ".ymaps_baloon_out";

              var content = $(params).append(...)

 

у вас content - пуст.

 $(".ymaps_baloon_out") ничего не возвращает

Отлично, данные теперь подгружаются, но, вот беда, не совсем так как надо. Они сразу грузятся в правый контейнер все. А при клике по элементам левого контейнера все исчезает. Подскажите пожалуйста как с этим быть!

Загружайте и добавляйте в отдельный контейнер.

 

И не забывайте смотреть и читать в консоли браузера,

как он передает вам "спасибо" за ваши ошибки в коде =)

Спасибо за помощь! В итоге можно все было проще сделать.
Вместо "myClusterer.events.add('click', function(e){" можно было использовать событие applyContent в макете балуна кластера. Аяксово подгружать данные не всех меток из кластера, а только первой. Что в итоге получилось можно по прежнему посмотреть на http://beta.zakvadrat.ru/search/?city=4&deal=1&group=-1&price=total&cur=rub&subtypes=None

У меня колесико мыши не в ту сторону работает!

Должно при вращении вверх увеличивать, а оно уменьшает!

Зделайте как было! Неудобно ;-(

У меня  там вообще мышиный скролл не используется Оо

Сделано. А по подгрузке подскажете, почему не грузит?