Javascript-форум (https://javascript.ru/forum/)
-   ExtJS (https://javascript.ru/forum/extjs/)
-   -   Есть ли декларативный аналог метода relayEvents()? (https://javascript.ru/forum/extjs/62957-est-li-deklarativnyjj-analog-metoda-relayevents.html)

khusamov 09.05.2016 17:35

Есть ли декларативный аналог метода relayEvents()?
 
Есть ли возможность декларативно описать таблицу пересылки событий?

Пример как я это делаю императивно:

initComponent: function() {
		var me = this;
		me.callParent(arguments);
		me.relayEvents(me.down("settings-trolleylist"), "print-trolley-label-button-click");
	},

nohuhu 09.05.2016 19:50

Зачем это вообще делать? Используйте ViewController.

khusamov 09.05.2016 20:11

Мне нужно из дочерних видов вытащить события в корневой вид.

Я в основном контроллере ловлю это событие:

Ext.define("Element.controller.Settings", {
	
	extend: "Ext.app.Controller",
	alias: "controller.app.settings",
	
	control: {
		"settings": {
			"trolleylist-print-label-button-click": "onTrolleyListPrintLabelButtonClick"
		}
	},
	
	onTrolleyListPrintLabelButtonClick: function(trolleyListView, trolleyItem) {
		var me = this;
		var printer = me.getApplication().getController("Printer");
		printer.print("trolleyitem", "barcode", trolleyItem);
	}
	
});

Или вы намекаете что надо обойтись без relayEvents? И сделать в control селектор вида "settings > settings-trolleylist"?

.

khusamov 09.05.2016 20:22

Если это делать в ViewController, то так что-ли сделать?

control: {
		"settings-trolleylist": {
			"print-label-button-click": function() {
				var args = Ext.Array.slice(arguments);
				args.unshift("trolleylist-print-label-button-click");
				this.getView().fireEvent.apply(this.getView(), args);
			}
		}
	},

Нагромождение какое-то вышло.

Жаль что в контроллере нет конфига relayEvents, чтобы просто настроить перенаправление событий и все.

.

khusamov 09.05.2016 20:28

Или может так? Немного короче вышло:

init: function() {
		var me = this;
		var trolleyListView = me.getView().down("settings-trolleylist");
		me.getView().relayEvents(trolleyListView, ["print-label-button-click"], "trolleylist-");
	}

nohuhu 09.05.2016 21:15

Я намекаю на то, что не нужно вообще использовать Ext.app.Controller для ловли событий в видах - для этого есть ViewController. И relayEvents в таком случае совершенно не нужно.

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

khusamov 09.05.2016 22:02

У меня в Ext.app.Controller организована печать на принтере (а именно прием заданий на печать, организация print-отчетов, подготовка данных к печати и т.п.). По-мойму вполне себе глобальная вещь. Зачем этот код плодить во всех видах?

Прием заданий я делаю путем ловли событий click из видов, в которых передается модель, которая затем поступает на вход отчета, и после обработки - в принтер. Это неправильно?

nohuhu 09.05.2016 22:34

Цитата:

Сообщение от khusamov (Сообщение 416095)
У меня в Ext.app.Controller организована печать на принтере (а именно прием заданий на печать, организация print-отчетов, подготовка данных к печати и т.п.). По-мойму вполне себе глобальная вещь. Зачем этот код плодить во всех видах?

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

Цитата:

Прием заданий я делаю путем ловли событий click из видов, в которых передается модель, которая затем поступает на вход отчета, и после обработки - в принтер. Это неправильно?
Технически такой подход допустим, и даже рекомендовался в версиях до 5.0, когда не было более удобных и менее проблематичных инструментов. Сейчас этот подход использовать настойчиво не рекомендуется, т.к. он чреват многими проблемами, для решения которых и предназначены ViewControllers.

В вашем случае я бы сделал так: ViewController ловит событие на дочерних компонентах и в свою очередь стреляет событие "printrequest" в контроллерном домене, передавая в аргументах объект со ссылкой на свою модель. Глобальный контроллер печати ловит события printrequest, берёт ссылку на модель, вытаскивает из неё нужные данные, ставит задание на печать и возвращает код подтверждения/ошибки.

Ext.define('My.view.FooPanel', {
    extend: 'Ext.panel.Panel',

    controller: 'foocontroller',

    viewModel: {
        ...
    },

    items: [{
        ...
    }],

    buttons: [{
        text: 'Печать',
        listeners: {
            click: 'onPrintButtonClick'
        }
    }]
});

Ext.define('My.view.FooPanelController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.foocontroller',

    onPrintButtonClick: function() {
        var options = {};

        options.viewModel = this.getViewModel();

        // Стреляем событие в домене Controller
        this.fireEvent('printrequest', options);

        if (options.success) {
            ...
        }
        else {
            Ext.Msg.alert('Ошибка печати', options.errorMsg);
        }
    }
});

Ext.define('My.controller.Print', {
    extend: 'Ext.app.Controller',

    listen: {
        controller: {
            '*': {
                printrequest: 'onPrintRequest'
            }
        }
    },

    onPrintRequest: function(options) {
        // Здесь *категорически* не стоит сохранять ссылку на модель,
        // это практически на 100% приведёт к проблемам.
        // Вытащили данные и больше модель не трогаем.
        var data = this.getDataFromViewModel(options.viewModel);

        if (this.spoolPrintJob(data)) {
            options.success = true;
        }
        else {
            options.errorMsg = '...';
        }
    }
});


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

khusamov 09.05.2016 23:43

Вообще-то я имел ввиду под моделью экземпляр класса Ext.data.Model, но это наверное не существенно.

Цитата:

...и возвращает код подтверждения/ошибки.
Это разве допустимо? А если на событие printrequest подпишутся два обработчика???

nohuhu 10.05.2016 03:31

А почему нет? :) На самом деле, это нормальный способ синхронной (и двусторонней!) передачи информации через события, безотносительно типа коммуницирующих сторон. Можно конечно и по-другому делать: контроллер Foo стреляет событие "printrequest", контроллер Print обрабатывает и в ответ стреляет событие "printresponse", которое Foo ловит. Или даже передавать ссылку на контроллер Foo в аргументах события, и т.п... Но я бы не рекомендовал так делать, т.к. можно легко насоздавать себе проблем с рекурсией, циклами событий и прочими прелестями односторонней синхронности. А уж про тестирование таких вариантов я просто молчу - фактически это будет означать взаимную зависимость обоих контроллеров друг от друга.

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

А если на событие printrequest подпишутся два обработчика, то объект options по возврату будет содержать потенциально конфликтную информацию. Но этот случай тоже довольно легко отловить:

onPrintRequest: function(options) {
    //<debug>
    if (options.success != null) {
        throw "Whoa there!";
    }
    //</debug>
}


На практике такое бывает довольно редко - мы же говорим о глобальных контроллерах, правда? Они ведь должны существовать в одном экземпляре, так ведь? И вы ведь это точно протестировали? ;)


Часовой пояс GMT +3, время: 07:02.