Ответ 1
На самом деле в коде есть по крайней мере четыре совершенно разные проблемы:
- Обработка области для вызовов методов внутри класса
- Неэффективность создания компонентов
- Обработка событий компонентов в контроллере
- Связь между контроллерами
Обработка области
Первый разрешается либо с помощью замыкания, либо с передачей в параметре области запроса 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 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*
}
});
Это может выглядеть как более сложный способ сделать что-то, но на самом деле его гораздо проще использовать в больших (иш) приложениях, и он решает основную проблему, которую мы имели: нет необходимости в жестком привязке между контроллеров, и вы можете тестировать каждый контроллер отдельно от других.
Подробнее в своем сообщении в блоге: События контроллера в Ext JS 4.2