Блог API Яндекс.Карт

Построение гибких интерфейсов с помощью макетов

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

Текстовые шаблоны на основе HTML-кода со специальными переменными позволяют создавать внешнее представление для объектов (верстку). Верстка, получаемая с помощью текстовых шаблонов, являются статичной, поэтому она не способна обрабатывать внешние события, например, щелчки мыши. Добавить поведение шаблону можно с помощью системы макетов, доступной в API Яндекс.Карт.

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

Макеты реализуют интерфейс YMaps.ILayout, который состоит из трех методов:

  • onAddToParent() – вызывается при добавлении макета в родительский элемент;
  • onRemoveFromParent() – вызывается при удалении макета из родительского элемента;
  • update() – обновляет макет.

Таким образом для создания собственного макета необходимо написать небольшой класс. Например, он может выглядеть следующим образом:

function ExampleLayout (context, map, owner) {
    var text = owner.name || "";
    this.$nodes = YMaps.jQuery("<div></div>").text(text);
 
    this.onAddToParent = function (parentNode) {
        this.$nodes.appendTo(parentNode);
    };
   
    this.onRemoveFromParent = function () {
        this.$nodes.remove();
        this.$nodes = null;
    };
   
    this.update = function () {}
}

В конструктор макета передается три параметра:

  • context – контекст;
  • map – указатель на карту;
  • owner – cсылка на объект (метку, балун, и пр.), для которого создается макет.

Эти параметры позволяют с легкостью манипулировать как самим объектом, так и его отображением на карте. В классе ExampleLayout используется только параметр owner, который позволяет получить доступ к названию объекта.

Применим созданный макет к метке. Создать макет класса ExampleLayout можно с помощью вспомогательного класса YMaps.LayoutTemplate. Макеты присваиваются объекту через стиль по аналогии с текстовыми шаблонами:

var placemark = new YMaps.Placemark(map.getCenter(), {
    style : {
        balloonContentStyle : {
            template : new YMaps.LayoutTemplate(ExampleLayout)
        }
    }
});
placemark.name = "Имя объекта";
map.addOverlay(placemark);

 

Классы YMaps.LaoutTemplate и YMaps.Template в целом очень похожи, потому что оба реализуют интерфейс YMaps.ITemplate. Отличаются эти классы только входными данными в конструктор: в одном случае это HTML – строка с переменными, а во втором – указатель на класс макета.

Можно реализовать интерфейс YMaps.ITemplate и получить свою собственную «фабрику» макетов. На вход конструктору может приходить представление в каком-то особенном формате (например, объекты определенного класса), а на выходе – макет. Главное, реализовать метод build(), который будет преобразовывать данные в макет.

Также в API есть несколько предопределенных интерфейсов макетов для соответствующих объектов: YMaps.IBalloonLayout для балуна, YMaps.IHintLayout для всплывающей подсказки и YMaps.IPlacemarkLayout для значка метки.

Реализовав интерфейс YMaps.IBalloonLayout, можно полностью изменить внешний вид балуна. Созданный макет будет обрабатывать событие закрытия балуна, а также можно будет установить контент с помощью стандартного метода setBalloonContent().

На странице примеров документации доступен пример макета для балуна.

Методы для установки контента setBalloonContent(), setHintContent() и setIconContent() в качестве аргумента также могут принимать макет. Вы можете отключить автозамену png-изображений на элементы div, которая в API Яндекс.Карт делается автоматически для браузера Internet Explorer 6.x, если создадите свой макет:

function SafeLayout (text) {
    this.onAddToParent = function (parentNode) {
        this.$nodes = YMaps.jQuery(text);
        this.$nodes.appendTo(parentNode);
    };
   
    this.onRemoveFromParent = function () {
        this.$nodes.remove();
        this.$nodes = null;
    };
   
    this.update = function () {}
}
 
var placemark = new YMaps.Placemark(map.getCenter());
placemark.setBalloonContent(new SafeLayout("<img src=\"http://img.yandex.net/i/www/logo.png\"/>"));
map.addOverlay(placemark);

 

Подведем итог. С помощью макетов можно изменять внешний вид объектов-оверлеев на карте и делать их интерактивными, обрабатывая различные события и действия пользователя. Макеты позволяют получить полный контроль над версткой объектов, что позволяет создавать любое внешнее представление для оверлеев. Область применения макетов не ограничивается только сменой внешнего вида, их можно применять и в других прикладных задачах, например, в статье Создание простого редактора меток макет использовался для создания интерфейса редактирования.

10 комментариев
Вопрос на 5 - в какой момент будет вызван onAddToParent в последнем примере.
Чем мне не нравяться Я.Карты - так это тем что контент всех балунов грузят изначально.
А это иногда десятки мегабайт
Метод onAddToParent() вызывается при открытии балуна, либо при вызове метода setBalloonContent().
Окей, тогда вопрос в догонку.
Есть ли стандартные интерфейсы по недопушению этого.
Вот есть у вас 100 маркеров по две картинки в балуне у каждого..
получается после старта карты грузим еще 200 картинок, большую часть который никто никогда не увидит..
Вроде как чисто филосовская проблема о том кто когда какие поля у кого читает..
(будем считать что это намек)
В этом случае не используйте метод setBalloonContent(), а задавайте конент для объектов через их специальные поля (name, description, metaDataProperty).
Сейчас вызов метода setBalloonContent() приведет к вызову метода onAddToParent(), даже если балун еще не был открыт. Возможно, это не совсем правильно. Мы подумаем над этим. Спасибо за ваше замечание.
Лучше поздно чем никогда.

В последнейм примере метод onAddToParent будет вызван только при показе балуна. Можно даже переделать пример, и изначально привязывать к объектам пустой макет, в котором уже реализовать логику подгрузки.

ЗЫ.
Саша, за свой ответ был покрыт матомпозором.
Не покрывай его логову пеплом, лучше переведи обычные, безмакетные, балуны на постзагрузку.
Хотя не факт что так уже не сделано( но 25 марта не было сделано )
А я вот, если честно, не понимаю про какую проблему ты говоришь.
Можешь разъяснить?
Посмотрел код. Наверно ты про то, что если задать контент текстом, уже при задании он превратится в дом элементы, что на много затратней по памяти. Это исправим в следующей версии.
в точку.
если у меня 100 обьектов с картинками, а то с флешками - после инициализации карты "по простому"...
на клиент пойдет грузиться ну мегабайт так 10..... :)