15

我是 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){
        ...
    }
});
4

2 回答 2

33

实际上,您的代码中至少存在四个明显不同的问题:

  • 类内方法调用的范围处理
  • 组件创建效率低下
  • 控制器中的组件事件处理
  • 控制器间通信

范围处理

第一个问题可以通过使用闭包或将范围参数传递给 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 中的控制器事件

于 2013-10-08T18:29:10.853 回答
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()}
                    })
                )
            })
        })
    })
},
于 2013-10-08T17:12:29.060 回答