extjs - 如何正确地从另一个控制器或闭包调用控制器方法 [英] extjs - how correctly call a controller method from another controller or closure

查看:25
本文介绍了extjs - 如何正确地从另一个控制器或闭包调用控制器方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 extjs 的新手,我正在使用 MVC 架构.

I'm new to extjs and I'm using the MVC architecture.

当我的应用程序引用控制器的方法时,我会这样做(在 MyApp.Application 中):

When my application references a method of a controller, I do it that way (in MyApp.Application):

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

已经很长了,但我认为这是方法.

It is already long, but I think this is the way to do.

当一个控制器在一个闭包中调用它自己的方法时,我被引导使用这个代码(在 MyApp.controller.Main 中:

When a controller calls it's own method in a closure, I was led to use this code (in 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() 不起作用.

I referenced the method with MyApp.app.getController('Main').controllerMethodTwo() because this is not refering to the controller object in the closure, and thus this..controllerMethodTwo()isn't working.

我觉得这完全令人费解,我希望有人有一个想法来解决 MyApp.app.getController-workaround.

I find this utterly convoluted, and I hope someone has an idea to get around that MyApp.app.getController-workaround.

感谢所有的建议,我可以优化我的代码并提出:

Thanks to all the suggestion I could optimize my code and came up with:

// 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){
        ...
    }
});

推荐答案

实际上,您的代码中至少有四个截然不同的问题:

Actually, there are at least four distinctly different problems in your code:

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

第一个是通过使用闭包或将范围参数传递给 Ajax 请求来解决的,如上述@kevhender.鉴于此,我主张编写更清晰的代码:

The first one is solved either by using a closure, or passing in the scope parameter to Ajax request, as @kevhender described above. Given that, I'd advocate writing clearer code:

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.这也几乎没有必要:您预先拥有项目列表并且您处于控制之中,因此让我们保持代码简洁且具有声明性,并一次性创建所有菜单项:

Component creation

The way you create menu items is less than efficient, because every menu item will be created and rendered to the DOM one by one. This is hardly necessary, either: you have the list of items upfront and you're in control, so let's keep the code nice and declarative, as well as create all the menu items in one go:

// 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 的大量资源.

What changes here is that we're iterating over the list and creating a new array of the soon-to-be menu item declarations. Then we add them all in one go, saving a lot of resources on re-rendering and re-laying out your storeMenu.

在每个菜单项上设置处理函数是完全没有必要的,而且效率很低,因为该函数所做的只是调用控制器.当一个菜单项被点击时,它会触发一个 click 事件——你需要做的就是连接你的控制器来监听这些事件:

It is completely unnecessary, as well as inefficient, to set a handler function on every menu item, when all this function does is call the controller. When a menu item is clicked, it fires a click event - all you need to do is to wire up your controller to listen to these events:

// 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 选择器,因为它们是全局的,如果您不够精确,您的控制器方法可能会从不需要的组件中捕获事件.

One best practice is to write the ComponentQuery selectors as finely grained as feasible, because they're global and if you're not precise enough your controller method may catch events from unwanted components.

目前这可能有点牵强,但由于您使用的是 Ext JS 4.2,您不妨利用我们在这方面添加的改进.在 4.2 之前,有一种首选(也是唯一的)方法可以从另一个控制器调用一个控制器的方法:

This is probably a bit far fetched at the moment, but since you're using Ext JS 4.2 you may as well take advantage of the improvements we've added in that regard. Before 4.2, there was a preferred (and only) approach to call one controller's methods from another controller:

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 中,我们添加了事件域的概念.这意味着现在控制器不仅可以侦听组件的事件,还可以侦听其他实体事件.包括他们自己的控制器域:

In Ext JS 4.2, we've added the concept of event domains. What it means is that now controllers can listen not only to component's events but to other entities events, too. Including their own controller domain:

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)应用程序中使用它要简单得多,并且它解决了我们遇到的主要问题:之间不需要硬绑定控制器,您可以独立于其他控制器测试每个控制器.

This may look like a more convoluted way to do things, but in fact it's a lot simpler to use in large(ish) apps, and it solves the main problem we've had: there is no need for hard binding between controllers anymore, and you can test each and every controller in isolation from others.

在我的博客文章中查看更多信息:Ext JS 4.2 中的控制器事件

See more in my blog post: Controller events in Ext JS 4.2

这篇关于extjs - 如何正确地从另一个控制器或闭包调用控制器方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆