假设我正在扩展标准的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 ) {
        }
    }
});

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中的库存类行为:子类化,类覆盖和实例配置。

子类化

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

override

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

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的所有实例(包括库存和自定义)的更改。这种方法主要用于补丁和修复。它可以用于向库存组件添加新功能,但是您会发现很难进行后续维护。

实例配置

也就是说,到目前为止,最流行的方法是通过设置配置选项和覆盖方法来实例化股票类并更改实例的行为:
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应用程序,因为它不允许您正确地对代码进行模块化,并且从长远来看更难维护。

声明性类

采取子类化方式的另一个好处是,它允许您将代码保持声明式而不是命令式:
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 config选项和initComponent方法覆盖,但是当实例化该类或其子类时,initComponent方法将在特定类当前的任何配置下运行。

因此,使用此类时,您可以选择覆盖实例的store配置:
var panel = new MyApp.view.Panel({
    store: 'BarStore'
});

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

或者只是回退到该类提供的默认配置:
var panel = new MyApp.view.Panel();

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

您也可以将其子类化,覆盖部分配置或行为,但不能覆盖所有内容:
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

声明式与命令式

将其与命令式方法进行比较,在命令式方法中,您每次必须为库存类实例指定完整的配置:
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)。您不能推广这种方法,也不能轻易使实例共享大多数行为,但细节有所不同-您将不得不为每个实例重复执行代码。

子类陷阱

这也将我们带到了人们在使用子类化时最常见的错误:只是半途而废。如果您看一下上面的代码,您可能会注意到这个确切的错误:
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';
        ...
    }
});

将您的代码与上面的声明性示例进行比较。区别在于,在您的类中,配置在实例化时发生,而在示例中,它在类声明时发生。

假设接下来,您将需要在应用程序的其他位置重用MainTree类,但是现在具有不同的存储或行为。使用上面的代码,您无法轻松做到这一点,您将不得不创建另一个类并覆盖initComponent方法:
Ext.define('MyApp.view.AnotherMainTree', {
    extend: 'MyApp.view.MainTree',

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

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

关于extjs - 重写Sencha ExtJS标准组件功能的步骤(Ext.tree.Panel和Ext.data.TreeStore作为两个示例),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18249715/

10-12 13:04
查看更多