重写Sencha ExtJS标准组件功能的步骤(Ext.tree.Panel和Ext.data.TreeStore作为两个示例)

Mac*_*ver 7 overriding extjs extjs4

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

我意识到有时候框架中有这么多功能,人们可能会忽略某个地方的配置而没有意识到他们可以通过标准实现解决问题.这可以通过更多框架经验得到纠正.把它放在一边,这些最基本的步骤是什么?

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

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

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 ) {
        }    
    }
});
Run Code Online (Sandbox Code Playgroud)

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) {
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

Ale*_*rev 34

概观

在不改变框架源的情况下,有三种方法可以在Ext JS 4.x中扩充库存类行为:子类化,类重写和实例配置.

子类

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

重写

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

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() {
        ...
    }
});
Run Code Online (Sandbox Code Playgroud)

这样,您可以应用将影响TreePanel的所有实例的更改,包括stock和custom.这种方法主要用于补丁和修复; 它可用于为库存组件添加新功能,但您会发现难以维持.

实例配置

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

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
    }
});
Run Code Online (Sandbox Code Playgroud)

这种做法在早期的Ext JS版本中得到了普及,并且仍在大量使用.我不建议将此方法用于新的4.x应用程序,因为它不允许您正确地模块化代码,并且从长远来看难以维护.

声明性类

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

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();
    }
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

var items = panel.getItems(); // Return all items from BarStore
Run Code Online (Sandbox Code Playgroud)

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

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

var items = panel.getItems(); // Return all items from FooStore
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

声明与命令

将其与必需的方法进行比较,您必须每次都为库存类实例指定完整配置:

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);
    }
});
Run Code Online (Sandbox Code Playgroud)

上面代码的最大缺点是,当类实例已经半开始初始化时(构造函数调用initComponent),一切都发生在类实例上.您无法概括此方法,并且您无法轻松地使实例共享大部分行为但细节不同 - 您必须为每个实例重复代码.

子类陷阱

这让我们看到了人们对子类化最常犯的错误:只走了一半.如果您查看上面的代码,您可能会注意到这个错误:

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';
        ...
    }
});
Run Code Online (Sandbox Code Playgroud)

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

假设您需要在应用程序的其他位置重用MainTree类,但现在需要使用不同的存储或行为.使用上面的代码,您无法轻松完成此操作,您将不得不创建另一个类并覆盖该initComponent方法:

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

    initComponent: function() {
        this.store = 'AnotherTreeNodes';
        ...
    }
});
Run Code Online (Sandbox Code Playgroud)

将其与上面的实例配置覆盖进行比较.不仅声明方法更容易编写和维护,而且它也是无限可测试的.