Sencha cmd — быстрый старт

Создание проекта:

— генерация проекта
sencha -sdk «D:\Install_new\sencha\ext-6.0.0-gpl\ext-6.0.0\» generate app App1 «E:\serp\work\_sencha\App1»
— или
sencha -sdk «D:\Install_new\sencha\ext-6.0.0-gpl\ext-6.0.0\» generate app -modern App1 «E:\serp\work\_sencha\App1»

— дальше перейти в сгенерированный проект
cd /D «E:\serp\work\_sencha\App1»

— запускаем проект, теперь он доступен по адресу http://localhost:1841/
sencha app watch

— билдим
sencha app build modern production
sencha app build modern production «d:/temp»
sencha app build modern testing

— дополнительный действия при билде(build.xml), в примере ниже копирование файла config.json



Как собрать проект под android:

— чтобы cordova была доступна из командной строки
npm install -g cordova

— подготавливаем проект, создает папку cordova и добавляет в app.json «builds»: «native», если builds не был определен

— «builds»: «native» надо будет еще подправить ручками, выбрать «platforms»
sencha cordova init com.iserv.cabinet Cabinet

«builds»: {
«native»: {
«packager»: «cordova»,
«cordova»: {
«config»: {
«platforms»: «android»,
«id»: «com.iserv.cabinet»,
«name»: «Cabinet»
}
}
}
},

— запускает приложение на android
sencha app run native

— билдит мобильную версию. Для android результат кладет в cordova\platforms\android\build\outputs\apk\android-debug.apk
sencha app build native

— установка на android
D:\android-sdk\platform-tools\adb kill-server
D:\android-sdk\platform-tools\adb install -r «cordova\platforms\android\build\outputs\apk\android-debug.apk»

— shell
D:\android-sdk\platform-tools\adb shell
D:\android-sdk\tools\ddms.bat
— какие устройства подключены к компьютеру
D:\android-sdk\platform-tools\adb devices

Запуск тестов Siesta из консоли с помощью PhantomJS

В проект добавлен скрипт run-siesta.js в папку с тестами. Он предназначен для того, чтобы с помощью PhantomJS запускать тесты Siesta. run-siesta.js использует API PhantomJS для запуска страницы c тестами и выводит в консоль и в текстовый файл siesta_log.txt логи, которые формируются на странице index.html.

Также в скрипте index.js изменен Harness.configure — в него добавлены дополнительные обработчики событий: testfinalize, testsuiteend и условие автозапуска autoRun: true.

При завершении тестов run-siesta.js парсит тестовую страницу на общее количество пройденных тестов, выводит общее затраченное время и возвращает соответствующий числовой идентификатор в консоль (0 – успех, 1 — ошибка).

Пример команды в консоли: D:\…\Omnis-ExtJS\Pir\Siesta\phantomjs run-siesta.js http://localhost:62681/Siesta/index.html

Для выполнения команды ‘phantomjs’ нужно установить сам PhantomJS — http://phantomjs.org/download.html, и прописать в PATH системы — путь к исполняемому файлу phantomjs.exe, или скопировать phantomjs.exe в папку с тестами D:\…\Omnis-ExtJS\Pir\Siesta\ и запускать команду: D:\…\Omnis-ExtJS\Pir\Siesta\phantomjs.exe run-siesta.js http://localhost:62681/Siesta/index.html На момент написания использовалась версия PhantomJS 2.0 — http://phantomjs.org/

Sencha CMD. Как правильно подключить собственные библиотеки.

Надо начать с того, что приложение, запущенное с помошью «sencha app watch», работает немного по-другому, чем приложение, собранное с помощью «sencha app build».

В случае «sencha app watch» нет никакого минификатора, загрузка библиотек идет из секции «js», определенной в файле app.json. Единственное, что собирается, это стили css, они кидаются в папку build/temp

В случае «sencha app build» есть минификатор, все библиотеки собираются в один файл. Пути для минификатора прописываюся в секции «classpath» файла app.json. При этом загрузку библиотек из секции «js» тоже никто не отменяет.

Из этого рождаются правила:
•если вы хотите подключить свою библиотеку, поместите ее в один из каталогов, определенный в «classpath», этим мы обеспечим то, что наша библиотека попадет в общий файл при работе минификатора.
•пропишите путь к этой библиотеки в секции «js», это позволит загружать ее при команде «sencha app watch».
•для прописанного пути в секции «js» поставьте «bootstrap»: true. Это нужно для того, чтобы эти пути учитывались только для «sencha app watch», но не попадали в финальный файл конфигурации после команды «sencha app build».

Если что-то сделать неправильно, то Extjs ругнется примерно такими словами: «Failed to resolve dependency It.app.Application…»
Пример подключения библиотеки it.core.js:

"classpath": [
     "app",
     "${toolkit.name}/src"
 ],
 
"js": [
     {
         "path": "app/it.core.js",
         "bootstrap": true
     },
     ...
 ],

setInterval или Ext.direct.PollingProvider

Иногда в приложениях требуется обращение к серверу по определенному интервалу времени. Стандартными средствами JavaScript’a это делается примерно так:

setInterval(function() {
     RPC.util.Format(function() {
           ...
     });
}, 5000);

Но в ExtJS есть более корректный подход, использовать Ext.direct.PollingProvider. Если кратко, то Ext.direct.PollingProvider — это провайдер, который сам запрашивает данные с сервера с помощью direct-функции по таймеру.

PN.RPC.POLLING_API = {
           type: 'itpolling',
           pollFn: 'RPC.util.Format,
           id: 'datePoller',
           interval: 2000,
           listeners: {
               data: function (provider, e, eOpts) {
                    // e.response - здесь хранится результат выполнения
                    var poll = Ext.direct.Manager.getProvider('datePoller');
                    poll.disconnect(); // если хотим завершить запросы
               }
           }
       };
       setInterval()
       // register the polling API to the PollingProvider.
       Ext.Direct.addProvider(PN.RPC.POLLING_API);

Данный способ отлично подходит если, например, нужно запрашивать какую-либо direct — функцию. В руководстве ExtJS type указан как polling, но здесь используется наши itpolling В нем переопределены некоторые методы для работы с нашим itdirect

!!!Внимание. Помните о том что большое количество запросов к серверу могут «положит» пул IIS

Код провайдера

Ext.define('It.direct.PollingProvider', {
    extend: 'Ext.direct.PollingProvider',
    alias: 'direct.itpollingprovider',
    requires: [
        'Ext.Ajax',
        'Ext.util.TaskRunner',
        'Ext.direct.ExceptionEvent'
    ],
    type: 'itpolling',
 
    /*
     * переопределен
     */
    onData: function (opt, success, response) {
        var me = this,
            i, len, events;
        if (success) {
            //events = me.createEvents(response);
            //for (i = 0, len = events.length; i < len; ++i) {
                me.fireEvent('data', me, response);
            //}
        } else {
            events = new (Ext).direct.ExceptionEvent({
                data: null,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            });
            me.fireEvent('data', me, events);
        }
    },
 
    /*
     * переопределен
     */
    onPollFn: function (result, event, success, options) {
        this.onData(null, success, {
            response: result
        });
    }
}) 

Работа с датами в ExtJS. Меняем локализацию

По умолчинию ExtJS применяет en-локализацию. Чтобы поменять ее на ru выполните следующие действия:

— в app.json добавляем следующие параметры:

"requires": [
    "font-awesome",
    "ext-locale" // пакет с локализациями
],
 
"locale": "ru" // устанавливаем культуру

Полный перечень культур можно посмотреть в папке ext\classic\local\overrides

— после этого запускаем команду app build

— открываем файл ext\classic\local\overrides и добавлем следующие параметры в объект Ext.locale.ru.form.field.Date

 ***
format: "d.m.Y",
dateFormat: "d.m.Y", // формат даты который выводиться в поле
submitFormat: 'm/d/Y H:i:s' // требуется для правильного конвертирования даты на клиенте
  ***

— далее в настройках прокси, для store, требуется указать следующий параметр

Ext.define('PN.store.FS_Service_Groups_FS_ServicesCollection_ListView',
{
    extend: 'Ext.data.Store',
    model: 'PN.model.FS_Service_Groups_FS_ServicesCollection_ListView',
    //autoLoad: true,
 
    remoteFilter: true,
    remoteSort: true,
    remoteGroup: true,
 
    proxy: {
        type: 'itdirect',
        api: {
            read:       PN.Domain.FS_Services.Query,
            create:     PN.Domain.FS_Services.Add,
            update:     PN.Domain.FS_Services.Update,
            destroy:    PN.Domain.FS_Services.Delete
        },
        reader: {
                successProperty: 'success',
                rootProperty: 'records',
            } ,
        writer : {
        dateFormat: "d.m.Y H:i:s" // даты на сервер будут возвращаться именно в этом формате
        }
    }
});

Splash screen для ExtJS приложения

Чтобы вывести сообщение об ожидании, при старте приложения ExtJS, требуется выполнить следующие действия.

Находим index.html файл добавляем в него следующий код

Загрузка ExtJS…

Данное сообщение будет выводиться да начала готовности Ext.Application.

!!! Внимание. Требуется наследоваться от It.app.Application

В файле app.js указать следующий параметр:

Ext.application({
    name: 'ImportSubstitution',
    ***
    splashscreen: true,
    ***

Ну вот и все!
Как это реализовано?

В базовом классе произведены следующие изменения:

/*
 * Объект для хранения splashscreen переменной
 */
splashscreen: null,
constructor: function () {
       var startExtJSMessage = Ext.get('start-extjs');
       if (startExtJSMessage)
           startExtJSMessage.hide();
 
       if (this.splashscreen == true) {
           // start the mask on the body and get a reference to the mask
           this.splashscreen = Ext.getBody().mask('Загрузка приложения...', 'splashscreen');
       }
       this.callParent(arguments);
   },

/*
 * Инициализация splashscreen
 */
initSplashScreen: function () {
    if (typeof this.splashscreen == 'object') {
        this.splashscreen.destroy();
    }
},

onProfilesReady: function () {
    var me = this;
    me.loadRemotingApi(() => {
        me.initControllers();
        me.onBeforeLaunch();
        me.initSplashScreen();
        me.finishInitControllers();
    });
},

Динамическое дерево в ExtJS modern

В данной статье пойдет речь о том, как создать динамическое дерево (TreeList) в ExtJS Modern. Возможно данная реализация есть в «коробке», но я ее не нашел.

Стандарный компонент treelist отлично работает, если в хранилище находится например 100 записей, но если их будет тысяча или более? У меня получилось так, что мобильный телефон стал заметно тормозить. И после долгих поисков решения в интернете, решил доработать компонент.

Первое что я сделал — это создал наследник от treelist

Ext.define('MobileService.view.routeList.items.MobileTreeList', {
    extend: 'Ext.list.Tree',
    xtype: 'mobiletreelist',
 
    privates: {
// expand - стоит строить дочернии элементы или нет. По умолчанию null createItem: function (node, parent, expand) { var isRootItem = node.get('isRootItem'); // требуется для определения корневого элемента (те записи которые видит пользователь на первом уровне) if (!isRootItem && !expand) return null; return this.callParent(arguments); }, // переопределен. Основной метод для добавления дочерних элементов onNodeExpand: function (node) { var item = this.getItem(node); var childNodes = node.childNodes; for (i = 0, len = childNodes.length; i < len; ++i) { child = childNodes[i]; if (!this.itemMap[child.internalId]) { var _item = item.getOwner().createItem(child, item, true); if (_item) { this.itemMap[child.internalId] = _item; item.insertItem(_item, null); } } } this.callParent(arguments); } } });

Второе, создаем наследник от Ext.list.TreeItem

/*
 * для вывода дерево документов и строк я создал собственный класс для TreeItem
 */
Ext.define('MobileService.view.routeList.items.RouteListItem', {
    extend: 'Ext.list.TreeItem',
    xtype: 'routelistitem',
 
    privates: {
        updateNode: function (node) {
            if (node) {
                var me = this,
                    map = me.itemMap,
                    childNodes, owner, len, i, item, child;
                me.element.dom.setAttribute('data-recordId', node.internalId);
                if (!map) {
                    childNodes = node.childNodes;
                    owner = me.getOwner();
                    me.itemMap = map = {};
                    for (i = 0, len = childNodes.length; i < len; ++i) {
                        child = childNodes[i];
                        item = owner.createItem(child, me);
                        if (item) { // добавили проверку на возможность создания ветки
                            map[child.internalId] = item;
                            me.insertItem(item, null);
                        }
                    }
                }
                me.setExpanded(node.isExpanded());
                me.doNodeUpdate(node);
            }
        }
    }
});

Третье, создаем данные для хранилища

var root = {
    expanded: true,
    children: [
        {
            text: 'item1',
            isRootItem: true,
            children: [
                ....
            ]
        },
        {
            text: 'item1',
            isRootItem: true,
            children: [
                ....
            ]
        }
    ]
};
var store = Ext.create('Ext.data.TreeStore', {
            root: root
});

Наконец и сам компонент

                xtype: 'mobiletreelist',
                height: '100%',
                defaults: {
                    xtype: 'routelistitem'
                }

Вроде все, должно работать. Все дочернии элементы будут отрисовывать динамически

Ext JS 6.0.0.640

Сжатие JS-файлов

Для создание одного js-файлай из нескольких можно использовать утилиту от Google — Closure Compiler

  1. устанавливаем Java Runtime Environment version 7, если у Вас её нет
  2. Скачиваем файл compiler.jar
  3. Распаковываем в папку например e:\compressionjs
  4. Для примера создаем в этой папке js — файл (например simple.js)
  5. // A simple function.
    function hello(longName) {
      alert('Hello, ' + longName);
    }
    hello('New User');
    
  6. Выполняем команду java -jar compiler.jar —js sample.js —js_output_file hello-compiled.js

Примечание: Если нужно по всей папке собрать, то можно выполнить java -jar compiler.jar —js=»*.js» —js_output_file hello-compiled.js

Подробнее о командах можно узнать java -jar compiler.jar —help

Сборка мобильного приложения

Сборка мобильного приложения осуществляется под разные регионы. Список регионов нужно уточнить в исходном коде. Для этого перейти в папку resources/usersetting.Настройка приложения под регион.

  • открываем файл resources/config.json. Находим свойство theme и меняем на имя региона
  • далее переходим в файл resources/userseting/имя региона/config.json. В данном файле хранятся свойства которые были переопределены. Установить параметр debug=false
  • перейти в файл app.json. В свойстве requires установить пакет региона (если он есть, но если нет, то нужно убрать все пакеты, которые начинаются с ext_)
  • выполняем команду sencha app build native testing
  • после завершения команды на рабочем столе должна появиться *.apk

Автоматическая сборка.

В корне приложения должен находиться файл build.js. Он написан под nodejs. Команда для запуска выглядит следующим образом

node cmd build имя_региона

Если нужно собрать приложения для конечного заказчика, то требуется дополнительно указать параметр production

node cmd build имя_региона production

Внимание!!! При сборке приложения под Казань требуется поставить crosswalk. Для других регионов данный плагин должен быть удален

Сборка с crosswalk

crosswalk — это дополнительный плагин для работы мобильного приложения в android’e, где версия браузера «старая» (нет поддержки css3)

Переходим в каталог cordova

cd cordova

cordova plugin add cordova-plugin-crosswalk-webview

Далее возвращаемся обратно в каталог с приложением и запускаем
node cmd build [имя региона]

Примечание: Главное запомнить, что при сборке для региона, где crosswalk не требуется нужно удалить данный плагин
cd cordova

cordova plugin remove cordova-plugin-crosswalk-webview