我是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
未引用闭包中的控制器对象,因此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){
...
}
});
答案 0 :(得分:33)
实际上,您的代码中至少存在四个截然不同的问题:
第一个是通过使用闭包或将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)应用中使用起来要简单得多,它解决了我们遇到的主要问题:不需要硬连接控制器了,您可以独立于其他控制器测试每个控制器。
在我的博文中查看更多内容:Controller events in Ext JS 4.2
答案 1 :(得分:2)
this
在success
回调中不起作用,因为它没有正确的范围。您的两个选择是:
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()}
})
)
})
})
})
},