extjs-как правильно вызвать метод контроллера из другого контроллера или закрытия

Я новичок в extjs, и я использую архитектуру MVC.

когда мое приложение ссылается на метод контроллера, я делаю это таким образом (в MyApp.Application):

Mb.app.getController('Main').myMethod();

это уже давно, но я думаю, что это способ сделать.

когда контроллер вызывает собственный метод в закрытии, я был вынужден использовать этот код (в MyApp.controller.Main:

controllerMethodOne: function(){
    Ext.Ajax.request({
        url: ...,
        params: ...,
        success: (function(response){
            list = Ext.JSON.decode(response.responseText);
            list.forEach(function(item){
                storeMenu.add(
                    Ext.create('Ext.menu.Item', {
                        text: item.text,
                        handler: function(el){MyApp.app.getController('Main').controllerMethodTwo()}
                    })
                )
            })
        })
    })
},

я ссылался на метод с MyApp.app.getController('Main').controllerMethodTwo(), потому что this refering к объекту контроллер закрытие, и таким образом this..controllerMethodTwo()не работает.

Я нахожу это совершенно запутанным, и я надеюсь, что у кого-то есть идея обойти это MyApp.app.getController-временное решение.

обновление

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

// in my controller
    mixins: ['Mb.controller.mixin.StoreMenu'],
    // I use that style of menus in two controllers thats why I use a mixin
    init: function() {
        this.control({
            '#vg_storeMenu menuitem': {
                click: this.onStoreMenuClicked
            }
        })
    },

// the controller mixin
Ext.define('Mb.controller.mixin.StoreMenu', {
    extend: 'Ext.app.Controller',
    buildStoreMenu: function(store_name){
        var storeMenu = Ext.ComponentQuery.query('#' + store_name + 'Menu')[0];
        Ext.Ajax.request({
            url: Paths.ajax + 'json.php',
            params: {list: store_name + 's'},
            success: (function(response){
            list = Ext.JSON.decode(response.responseText);
            items = Ext.Array.map(list, function(item) {
                return {
                    xtype: 'menuitem',
                    text: item.text
                }
            });
                storeMenu.add(items);
            })
        })
    },
    onStoreMenuClicked: function(el){
        ...
    }
});

2 ответов


На самом деле в вашем коде есть по крайней мере четыре совершенно разные проблемы:

  • обработка области для внутриклассных вызовов методов
  • неэффективность создания компонентов
  • обработка событий компонентов в контроллере
  • связь между контроллерами

объем обработки

первый решается либо с помощью закрытия, либо путем передачи параметра scope в Ajax-запрос, как @kevhender описанный выше. Учитывая это, я бы выступил за написание более четкого кода:

controllerMethodOne: function() {
    Ext.Ajax.request({
        url: ...,
        params: ...,
        scope: this,
        success: this.onMethodOneSuccess,
        failure: this.onMethodOneFailure
    });
},

// `this` scope is the controller here
onMethodOneSuccess: function(response) {
    ...
},

// Same scope here, the controller itself
onMethodOneFailure: function(response) {
    ...
}

создание компонента

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

// I'd advocate being a bit defensive here and not trust the input
// Also, I don't see the `list` var declaration in your code,
// do you really want to make it a global?
var list, items;

list  = Ext.JSON.decode(response.responseText);
items = Ext.Array.map(list, function(item) {
    return {
        xtype: 'menuitem',
        text: item.text
    }
});

// Another global? Take a look at the refs section in Controllers doc
storeMenu.add(items);

что изменения здесь в том, что мы перебираем list и создание нового массива объявлений пунктов меню в ближайшее время. Затем мы добавляем их все за один раз, экономя много ресурсов на повторном рендеринге и повторной выкладке вашего storeMenu.

компонент даже обращение

совершенно ненужно, так же, как неэффективно, установить функцию обработчика на каждом пункте меню, когда вся эта функция делает вызывает регулятор. При нажатии на пункт меню он запускает click событие - все, что вам нужно сделать, это подключить контроллер для прослушивания этих событий:

// Suppose that your storeMenu was created like this
storeMenu = new Ext.menu.Menu({
    itemId: 'storeMenu',
    ...
});

// Controller's init() method will provide the wiring
Ext.define('MyController', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            // This ComponentQuery selector will match menu items
            // that descend (belong) to a component with itemId 'storeMenu'
            '#storeMenu menuitem': {
                click: this.controllerMethodTwo
            }
        });
    },

    // The scope is automatically set to the controller itself
    controllerMethodTwo: function(item) {
        ...
    }
});

одна из лучших практик-написать селекторы ComponentQuery как можно более мелкозернистые, потому что они глобальны, и если вы недостаточно точны, ваш метод контроллера может перехватывать события от нежелательных компонентов.

связь между контроллерами

это, вероятно, немного надуманно на данный момент, но поскольку вы используете Ext JS 4.2, вы можете также воспользоваться из улучшений, которые мы добавили в этом отношении. До 4.2 был предпочтительный (и единственный) подход для вызова методов одного контроллера из другого контроллера:

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

    methodFoo: function() {
        // Need to call controller Bar here, what do we do?
        this.getController('Bar').methodBar();
    }
});

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

    methodBar: function() {
        // This method is called directly by Foo
    }
});

в Ext JS 4.2 мы добавили концепцию доменов событий. Это означает, что теперь контроллеры могут прослушивать не только события компонента, но и другие события сущностей. Включая собственный домен контроллера:

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

    methodFoo: function() {
        // Effectively the same thing as above,
        // but no direct method calling now
        this.fireEvent('controllerBarMethodBar');
    }
});

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

    // Need some wiring
    init: function() {
        this.listen({
            controller: {
                '*': {
                    controllerBarMethodBar: this.methodBar
                }
            }
        });
    },

    methodBar: function() {
        // This method is called *indirectly*
    }
});

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

подробнее в моем блоге: события контроллера в Ext JS 4.2


this не работает в success обратный вызов, потому что он не имеет правильной области. Ваши 2 варианта:

1: Создайте переменную в начале функции для ссылки в обратном вызове:

controllerMethodOne: function(){
    var me = this;
    Ext.Ajax.request({
        url: ...,
        params: ...,
        success: (function(response){
            list = Ext.JSON.decode(response.responseText);
            list.forEach(function(item){
                storeMenu.add(
                    Ext.create('Ext.menu.Item', {
                        text: item.text,
                        handler: function(el){me.controllerMethodTwo()}
                    })
                )
            })
        })
    })
},

2: Используйте scope конфигурации Ext.Ajax.request звоните:

controllerMethodOne: function(){
    Ext.Ajax.request({
        url: ...,
        params: ...,
        scope: this,
        success: (function(response){
            list = Ext.JSON.decode(response.responseText);
            list.forEach(function(item){
                storeMenu.add(
                    Ext.create('Ext.menu.Item', {
                        text: item.text,
                        handler: function(el){me.controllerMethodTwo()}
                    })
                )
            })
        })
    })
},