Новые возможности DVelum 0.9.2.7 Наследование компонент, создание событий, использование вложенных проектов.

В данном рецепте рассмотрим новые возможности платформы, познакомимся с наследованием объектов, созданием методов и событий, коснемся работы с Google Maps API, подключением сторонних плагинов и вложенных проектов.

Описание примера

Создание базы данных

Создание интерфейса

Создание компонента для работы с картами

Подключение компонента к проекту

Описание примера

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

Создание базы данных

Зайдем в интерфейс управления ORM и создадим новый объект geouser, который будет содержать информацию об имени пользователя и его координатах c полями:

  • name - varchar 255
  • latitude - decimal(12,6)
  • longitude - decimal(12,6)

Создание интерфейса

Зайдем в управление модулями административной панели и сгенерируем модуль для объекта geouser:

Проверим интерфейс, который сгенерировала система:

Теперь нам необходимо доработать редактор пользователей таким образом, чтобы можно было указать координаты, кликнув на карте.

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

Создание компонента для работы с картой

Зайдем в дизайнер интерфейсов и создадим проект компонента, назовем его map, сразу же перейдем к настройкам проекта и изменим пространства имен:

  • Project classes namespace: appMapClasses
  • Project run namespace: appMapRun

Нам понадобится JS-плагин для работы с картами, он уже есть в системе в папке js/lib/ext4/ux/GMapPanel.js, его необходимо подключить к проекту (его документация http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.ux.GMapPanel).

Добавим в проект окно (Window), назовем его "mapWindow”.

Воспользуемся новыми возможностями платформы и объявим окно наследником Ext.Window. Для этого в его свойствах выставим isExtended: true, после чего станет возможно определение собственных событий и методов.

Так же изменим свойство layout: layout: fit.

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

В первую очередь подключим Google map API в actionJs проекта. Откроем редактор JS-кода и вставим код динамической подгрузки API:

				
Ext.onReady(function(){
 Ext.Loader.loadScriptFile('https://www.google.com/jsapi',
  function(){
      google.load("maps", "3", {
            other_params:"sensor=false",
            callback : function(){
                   // Google Maps are loaded. Place your code here
            }
      });
   } , Ext.emptyFn , null , false);
});

Теперь необходимо инициализировать UX-компонет и добавить окну (mapWindow) несколько методов.

Переопределим метод initComponent окна mapWindow.

Перейдем на вкладку методов панели свойств и нажмем кнопку “Add Method”. В окне, запрашивающем имя нового метода, укажем initComponent.

Метод будет содержать следующий код:


// выбранные координаты адреса	 	
this.selectedLatLng = false;
// массив маркеров карты для удобства манипуляций
this.markersArray = [];
// инициализируем компонент Ext.ux.GMapPanel, добавляем 
// обработчик события 'mapready'. как только карта 
// инициализируется будет вызван метод prepareMap, который мы 
// опишем в mapWindow
this.gmapPanel = Ext.create('Ext.ux.GMapPanel',{
        center: {
           geoCodeAddr: '4 Yawkey Way, Boston, MA, 02215-3409, USA'
       },
       listeners:{
             'mapready':{
                fn:this.prepareMap,
                scope:this
             }
       }
});
// вызываем отрисовку компонент назначенных в дизайнере
// необходимо т.к. мы переопределили initComponent, отвечающий за
// этот процесс
this.addDesignerItems();


if(Ext.isEmpty(this.items)){
   this.items = [];
}
// добавляем панель с картой в окно
this.items.push(this.gmapPanel);
// передаем обработку в  initComponent  родительского объекта
this.callParent();

Добавим в mapWindow два системных метода для управления маркерами:

removeMarkers (удаление маркеров с карты) с кодом:


// пробегаем по списку маркеров и отключаем их	 			
for (var i = 0; i < this.markersArray.length; i++ ) {
   this.markersArray[i].setMap(null);
}
// очищаем список маркеров
this.markersArray = [];

addMarker (добавление маркера на карту) с параметрами : float lat, float lng кодом:

// создаем объект координат  google map API	 			
var myLatlng = new google.maps.LatLng(lat,lng);
//создаем маркер  google map API
var marker = new google.maps.Marker({
  position: myLatlng
});
// добавляем маркер в список
this.markersArray.push(marker);
// добавляем маркер на карту, которая находится в панели gmapPanel
marker.setMap(this.gmapPanel.gmap);

Добавляем системный метод prepareMap, который будет запущен после инициализации карты (мы описали его вызов в initComponent).

С параметрами: Ext.ux.GMapPanel gMapPanel, google.maps.Map map кодом:

var me = this;
// выставляем маркер текущей позиции (если она была передана в конструкторе)
if(!Ext.isEmpty(this.currentPosition) && this.currentPosition!==false){
this.addMarker(this.currentPosition.lat ,this.currentPosition.lng);
this.childObjects.selectBtn.enable();
map.setCenter(new google.maps.LatLng(this.currentPosition.lat , this.currentPosition.lng));
}
// обработчик клика по карте, сохраняем позицию переносим маркер в новую точку
google.maps.event.addListener(map, 'click', function(event) {
  me.removeMarkers();
  me.addMarker(event.latLng.mb , event.latLng.nb);
  me.selectedLatLng = event.latLng;
  me.childObjects.selectBtn.enable();
});

Список методов будет выглядеть следующим образом:

Добавим нижний тулбар. Положим в dockedItems компонент toolbar -> Panel, назовем его buttonsBar, выставим для этого компонента следующие свойства:

  • dock: bottom (расположит тулбар внизу)
  • ui: footer (дополнительная настройка кнопок размещенных в этой панели)

Добавим кнопку выбора текущего положения.

Разместим компонент toolbar -> Fill, как дочерний элемент buttonsBar, назовем его bBarFill.

Разместим компонент Button, как дочерний элемент buttonsBar, назовем кнопку selectBtn.

Установим следующие свойства кнопки: disabled:true (по умолчанию отключена).

Так же укажем текст на кнопке. В этот раз воспользуемся еще одним нововведением платформы - возможностью задавать Javascript вместо текстовых значений свойств. Таким образом, вместо текста «применить» мы вставим соответствующее значение из словаря локализации, напишем [js:] appLang.APPLY.

Текст на кнопке будет отображаться иcходя из настроек локали (словари можно посмотреть в папке system/lang/ [en.php / ru.php]). Таким образом, легко создавать локализацию проектов дизайнера интерфейсов.

Добавим обработчик события клика по кнопке выбора.

Для этого откроем вкладку Events на панели свойств кнопки, добавим обработчик click:

if(this.selectedLatLng!==false){
  this.fireEvent('locationSelected', {
     lat:this.selectedLatLng.mb,
     lng:this.selectedLatLng.nb
   });
}
this.close();

В этом коде мы проверяем, выбраны ли координаты, если да, то бросаем событие 'locationSelected', с событием передаем координаты последней выбранной точки. Событие мы опишем чуть дальше.

После того как добавлен обработчик кнопки необходимо объявить событие ‘locationSelected’, чтобы на него могли подписываться слушатели. Это еще одна новая возможность системы - задание собственных событий для расширенных компонент.

Зайдем в свойства окна mapWindow, переключимся на вкладку Events, нажмем кнопку ‘Add Event’, укажем ‘locationSelected’ в качестве имени. Поскольку обработчик события нам не нужен, после создания просто закрываем окно редактора события.

На этом создание завершено. Сохраним и закроем проект.

Подключение компонента к проекту

Подключим компонент к нашему редактору пользователей, для этого откроем проект geouser.designer.dat и подключим к нему наш компонент (Project config -> Add project file) map.designer.dat.

Убедимся, что вложенный проект успешно подключен - нажмем кнопку Related project items, должно открыться окно, в котором будет список компонентов, подключенных подпроектов и их структура.

Теперь необходимо доработать окно редактора пользователя таким образом, чтобы координаты подставлялись из нашего компонента с картами. Первым делом выставим свойства readOnly:true для полей editWindow_latitude и editWindow_longitude.

Внутрь editWindow_generalTab добавим компонент Form Field ->Field Container, назовем его locationContainer, выставим ему свойство layout: hbox.

Внутрь locationContainer добавим еще один Field Container, назовем его cordsContainer.

Перекинем поля editWindow_latitude, editWindow_longitude внутрь этого контейнера (cordsContainer).

Внутрь editWindow_generalTab добавим кнопку (Button), назовем ее setLocationBtn, установим ей свойство text: Set location.

Получим следующую структуру компонент:

Если выбрать editWindow, и нажать show window, получим следующую картину:

Осталось определить реакцию на событие click для кнопки setLocationBtn.

Откроем вкладку Events на панели свойств setLocationBtn, найдем и отредактируем нужное событие:

// получим ссылку на форму app.EditWindow.getForm()
// второй вызов  getForm()  возвращает Ext.form.Basic
var form = this.getForm().getForm();
// получаем значения координат из полей формы
var lat = form.findField('latitude').getValue();
var lng = form.findField('longitude').getValue();

// если координаты заданы, определяем позицию маркера на карте
var curPosition = false;
if(lat!==0 && lng!==0){
  curPosition = {'lat':lat,'lng':lng};
}
// создаем экземпляр нашего компонента, передаем настройки
// вешаем слушателя события 'locationSelected', который заполнит форму новыми значениями

Ext.create('appMapClasses.mapWindow',{
   currentPosition:curPosition,
   listeners:{
       'locationSelected':function(cords){
                 form.findField('latitude').setValue(cords.lat);
                 form.findField('longitude').setValue(cords.lng);
       }
   }
}).show();

Сохраняем и закрываем проект, переходим в модуль geouser и наблюдаем результат:

comments powered by Disqus