覆盖 Sencha ExtJS 标准组件功能的步骤(以 Ext.tree.Panel 和 Ext.data.TreeStore 为例) [英] Steps to overriding Sencha ExtJS standard component functionality (Ext.tree.Panel & Ext.data.TreeStore as two examples)

查看:12
本文介绍了覆盖 Sencha ExtJS 标准组件功能的步骤(以 Ext.tree.Panel 和 Ext.data.TreeStore 为例)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在扩展一个标准的 Sencha ExtJS 4 小部件/组件,并且我发现了一堆不能按照我想要的方式工作的东西,或者它们可能只是坏了而 Sencha 还没有来得及修复组件的问题.我将使用 Sencha ExtJS Ext.tree.Panel 和 Ext.tree.Store 作为两个示例组件.覆盖构造函数、配置、属性、方法和事件的最基本步骤是什么,以便我可以在不修改我当前使用的核心 ExtJS 4 框架 JS 文件的情况下找到并修复该组件的问题?

Suppose I am extending a standard Sencha ExtJS 4 widget/component, and I found a bunch of things that don't work the way I want them to, or perhaps they are just broken and Sencha hasn't gotten around to fixing the issues with the component yet. I'm just going to use the Sencha ExtJS Ext.tree.Panel and Ext.tree.Store as two example components. What are the most basic steps to overriding the constructor, configs, properties, methods and events so I can find and fix the issues with that component without modifying the core ExtJS 4 framework JS file I'm currently using?

我意识到有时框架中有太多功能,人们可能会忽略某处的配置,而没有意识到他们可以通过标准实现来解决问题.这是可以通过更多的框架经验来纠正的.暂且不论,这些最基本的步骤是什么?

I realize that sometimes there is so much functionality in the framework, that one might overlook a config somewhere and not realize they can fix the issue with a standard implementation. And that's something that can be corrected with more experience with the framework. Putting that aside here, what would be these most basic steps?

假设我们从这两个实现开始,并从最基础的开始.

Suppose we start with these two implementations and start with the very basics.

仅供参考:我确实使用 Ext.Direct 服务器端堆栈无需太多努力就获得了这两个组件的核心功能,并且我可以解释所有与 Sencha ExtJS Ext.tree.Panel 组件的跨浏览器兼容问题使用 IE、Mozilla Firefox 和 Google Chrome,但我可能会花太多时间问其他问题.我并不是说 IE 首先是陈规定型的,因为所有这些浏览器都存在 Ext.tree.Panel 组件的问题.我宁愿在这里学习如何钓鱼,这样我才能钓到自己的鱼.一旦我更好地理解了这些与树相关的类,我就会提出更具体的问题.

FYI: I got the core features of these two components working without too much effort really using the Ext.Direct server side stack, and I could explain all of the cross browser compatible issues with the Sencha ExtJS Ext.tree.Panel component with IE, Mozilla Firefox and Google Chrome, but I would probably spend too much time asking those other questions. And I'm not saying IE first to be stereotypical, because all of these browsers have their issues with the Ext.tree.Panel component. I'd rather learn how to fish here, so I can catch my own fish. Once I understand these tree related classes better, I will ask more specific questions.

http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.TreeStore

自定义 Ext.data.TreeStore 实现:

Ext.define('MyApp.store.TreeNodes', {
    extend: 'Ext.data.TreeStore',
    xtype: 'store-tree-nodes',
    model : 'MyApp.model.TreeNode',
    proxy: {
        type: 'direct',
        directFn: Tree_Node_CRUD.read,
        reader: {
            root: 'data'
        }
    },
    nodeParam: 'node',
    parentField: 'parentId',
    root: {
        text: 'root',
        id: '0',
        expanded: true
    },
    autoLoad: false,
    single: true,
    listeners: {
        beforeload: function(store, operation, options) {
        },
        append: function( thisNode, newChildNode, index, eOpts ) {
        }    
    }
});

http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.tree.Panel

自定义 Ext.tree.Panel 实现:

Ext.define('MyApp.view.MainTree', {
    extend: 'Ext.tree.TreePanel',
    xtype: 'view-main-tree',
    requires: [
        'MyApp.store.TreeNodes'
    ],
    initComponent: function() 
    {        
        this.store = 'TreeNodes';
        this.superclass.initComponent.call(this);
    },
    animate: false,
    title: 'Tree',
    rootVisible: true,
    collapsible: true,   
    dockedItems: [{
        xtype: 'toolbar',
        items: [{
            text: 'Open Node'
        }, {
            text: 'Create Node'
        }, {
            text: 'Delete Node'
        }, {
            text: 'Expand All'
        }, {
            text: 'Collapse All'
        }]
    }], 
    listeners: {
        afterrender: function() {
        },
        itemclick: function(view, node, item, index, e) {
        },
        afteritemexpand: function() {  //node, index, item, eOpts) {
        },
        afteritemcollapse: function() { //node, index, item, eOpts) {
        }
    }
});

推荐答案

概述

在 Ext JS 4.x 中可以通过三种方式在不改变框架源的情况下增加股票类的行为:子类化、类覆盖和实例配置.

Overview

There are three ways of augmenting the stock classes behavior in Ext JS 4.x without changing the framework source: subclassing, class overriding, and instance configuration.

当您需要创建为您的应用程序量身定制的自定义组件时,您需要执行子类化.这实际上就是您在上面的代码中所做的:您正在获取一个库存组件,更改其行为以满足您的需要,并将其用作新组件.重要的一点是,通过子类化您不会改变库存组件的行为,因此您可以同时使用自定义和库存组件.

Subclassing is what you do when you need to create a custom component tailored for your application. This in fact is what you are doing in the code above: you're taking a stock component, changing its behavior to suit your needs, and using it as a new component. The important point is that by subclassing you are not changing the stock component's behavior so you can use both custom and stock components.

覆盖是另一种改变股票类行为的方法:

Overriding is another approach that will change the behavior of the stock class:

Ext.define('MyApp.tree.TreePanel', {
    override: 'Ext.tree.Panel',

    // Stock fooMethod has a bug, so we are
    // replacing it with a fixed method
    fooMethod: function() {
        ...
    }
});

通过这种方式,您可以应用将影响所有 TreePanel 实例的更改,包括库存和自定义.这种方法主要用于补丁和修复;它可用于为库存组件添加新功能,但您会发现以后很难维护.

This way you can apply the changes that are going to affect all instances of the TreePanel, both stock and custom. This approach is mostly used for patches and fixes; it can be used for adding new features to the stock components but you will find it harder to maintain down the road.

也就是说,目前最流行的方法是通过设置配置选项和覆盖方法来实例化股票类并更改实例的行为:

That said, the most popular approach so far is to instantiate the stock classes and change the behavior of the instances by setting config options and overriding methods:

var tree = new Ext.tree.Panel({
    fooConfig: 'bar', // override the default config option

    fooMethod: function() {
        // Nothing wrong with this method in the stock class,
        // we just want it to behave differently
    }
});

这种做事方式在早期的 Ext JS 版本中很流行,并且仍然被大量使用.我不建议将这种方法用于新的 4.x 应用程序,因为它不允许您正确地模块化您的代码,并且从长远来看更难维护.

This way of doing things was popularized in earlier Ext JS versions and is still heavily used. I do not recommend this approach for new 4.x applications, because it does not allow you to modularize your code properly and is harder to maintain in the long run.

采用子类化方式的另一个好处是它允许您保持代码声明性而不是命令性:

Another benefit of going the subclassing way is that it allows you to keep your code declarative instead of imperative:

Ext.define('MyApp.view.Panel', {
    extend: 'Ext.panel.Panel',

    store: 'FooStore',

    // Note the difference with your code: 
    // the actual function reference
    // will be resolved from the *object instance*
    // at the object instantiation time
    // and may as well be overridden in subclasses
    // without changing it here
    listeners: {
        itemclick: 'onItemClick'
    },

    initComponent: function() {
        var store = this.store;

        if (!Ext.isObject(store) || !store.isStore) {
            // The store is not initialized yet
            this.store = Ext.StoreManager.lookup(store);
        }

        // You don't need to address the superclass directly here.
        // In the class method scope, callParent will resolve
        // the superclass method and call it.
        this.callParent();
    },

    // Return all items in the store
    getItems: function() {
        return this.store.getRange();
    },

    onItemClick: function() {
        this.doSomething();
    }
});

上述类声明MyApp.view.Panel的所有实例共享,包括store配置选项和initComponent 方法覆盖,但是当您实例化此类或其子类时,initComponent 方法将对特定类的当前配置进行操作.

The above class declaration is shared by all instances of the MyApp.view.Panel, including both the store config option and the initComponent method override, but when you instantiate this class or its subclasses, initComponent method will operate on whatever configuration is current for the particular class.

因此,在使用此类时,您可以选择覆盖 instancestore 配置:

So when using such class, you will have a choice of either overriding the store config for the instance:

var panel = new MyApp.view.Panel({
    store: 'BarStore'
});

var items = panel.getItems(); // Return all items from BarStore

或者只是回退到类提供的默认配置:

Or just falling back to the default configuration provided by the class:

var panel = new MyApp.view.Panel();

var items = panel.getItems(); // Return all items from FooStore

您也可以将其子类化,覆盖部分配置或行为,但不是全部:

You can also subclass it, overriding part of the config or behavior, but not everything:

Ext.define('MyApp.view.NewPanel', {
    extend: 'MyApp.view.Panel',

    // For this Panel, we only want to return first 10 items
    getItems: function() {
        return this.store.getRange(0, 9);
    },

    onItemClick: function() {
        this.doSomethingElse();
    }
});

var panel = new MyApp.view.NewPanel();

var items = panel.getItems(); // Return first 10 items from FooStore

声明式与命令式

将其与每次都必须为 stock 类实例指定完整配置的命令式方法进行比较:

Declarative vs Imperative

Compare that to the imperative approach in which you will have to specify the full configuration for the stock class instance every time:

var panelFoo = new Ext.panel.Panel({
    initComponent: function() {
        this.store = Ext.StoreManager.lookup('FooStore');

        // Note that we can't use this.callParent() here
        this.superclass.initComponent.call(this);
    }
});

var panelBar = new Ext.panel.Panel({
    initComponent: function() {
        this.store = Ext.StoreManager.lookup('BarStore');
        this.superclass.initComponent.call(this);
    }
});

上面代码的最大缺点是,当类实例已经半初始化(initComponent由构造函数调用)时,一切都会发生在类实例上.您无法概括这种方法,也无法轻松地让实例共享大部分行为但在细节上有所不同——您将不得不为每个实例重复代码.

The biggest disadvantage of the code above is that everything happens to the class instance when it is already halfway initialized (initComponent is called by the constructor). You can't generalize this approach, and you can't easily make instances share most of the behavior but differ in details -- you will have to repeat the code for every instance.

这给我们带来了人们在子类化中最常见的错误:只走一半.如果您查看上面的代码,您可能会注意到这个确切的错误:

This brings us to the most common mistake people make with subclassing as well: going just half way. If you take a look at your code above, you may notice this exact mistake:

Ext.define('MyApp.view.MainTree', {
    extend: 'Ext.tree.TreePanel', // You're using subclassing

    initComponent: function() {

        // But here you are assigning the config options
        // to the the *class instance* that has been
        // instantiated and half way initialized already
        this.store = 'TreeNodes';
        ...
    }
});

将您的代码与上面的声明性示例进行比较.不同之处在于,在您的类中,配置发生在实例化时间,而在示例中,它发生在类声明时间.

Compare your code with the declarative example above. The difference is that in your class, configuration happens at instantiation time while in the example it happens at the class declaration time.

假设接下来您需要在应用程序的其他地方重用 MainTree 类,但现在使用不同的存储或行为.使用上面的代码,您不能轻松地做到这一点,您必须创建另一个类并覆盖 initComponent 方法:

Suppose that down the road you will need to reuse your MainTree class elsewhere in the application, but now with a different store, or behavior. With the code above you can't do that easily and you will have to create another class and override the initComponent method:

Ext.define('MyApp.view.AnotherMainTree', {
    extend: 'MyApp.view.MainTree',

    initComponent: function() {
        this.store = 'AnotherTreeNodes';
        ...
    }
});

将其与上面的实例配置覆盖进行比较.声明式方法不仅更易于编写和维护,而且更易于测试.

Compare that to the instance config override above. Not only the declarative approach is easier to write and maintain, but it is infinitely more testable as well.

这篇关于覆盖 Sencha ExtJS 标准组件功能的步骤(以 Ext.tree.Panel 和 Ext.data.TreeStore 为例)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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