超越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)

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

问题描述

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



我意识到有时在框架中有这么多的功能,可能会忽略一个配置,而不知道他们可以通过标准实现解决问题。这是可以通过更多的框架经验来纠正的。在这里,这些最基本的步骤是什么?



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



FYI:我得到这两个组件的核心功能,没有太多的努力,真的使用Ext.Direct服务器端堆栈,我可以解释所有与Sencha ExtJS Ext的跨浏览器兼容的问题。 tree.Panel组件与IE,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',{
extends:'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 :functio n(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',{
extends:'Ext.tree.TreePanel',
xtype:'view-main-tree',
require:[
'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:'全部展开'
},{
文本:'全部折叠'
}]
}],
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中的库类行为有三种方法,而无需更改框架源:子类,类覆盖和实例配置。



Subclassing



子类化是当您需要为应用程序量身打造的自定义组件时所做的工作。这实际上是你在上面的代码中所做的:你正在使用一个库存组件,改变它的行为以满足你的需要,并把它作为一个新的组件。重要的一点是,通过子类化,您不会更改股票组件的行为,因此您可以使用自定义和股票组件。



覆盖



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

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

//库存fooMethod有一个错误,所以我们是
//用一个固定方法
fooMethod:function(){
...
}
});

通过这种方式,您可以应用将影响所有实例的更改的TreePanel,股票和定制。这种方法主要用于补丁和修复;



实例配置



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

  var tree = new Ext.tree.Panel({
fooConfig:'bar',//覆盖默认配置选项

fooMethod:function(){
//在库存类中没有任何错误,
//我们只是希望它的行为不同
}
});

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



声明式类



继承子类方式的另一个好处是它允许你保持代码声明而不是必须:

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

store:'FooStore ',

//注意与代码的区别:
//实际的函数引用
//将从*对象实例中解析*
//在对象实例化时间
//并且也可能被覆盖在子类
//中,而不在这里更改
listeners:{
itemclick:'onItemClick'
} ,

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

if(!Ext.isObject(store)||!store.isStore) {
//该商店尚未初始化
this.store = Ext.StoreManager.lookup(store);
}

//您不需要直接在这里寻址超类。
//在类方法范围内,callParent将解析
//超类方法并调用它。
this.callParent();
},

//返回商店中的所有商品
getItems:function(){
return this.store.getRange();
},

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

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



所以当使用这样的类时,你可以选择覆盖 store config对于实例

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

var items = panel.getItems(); //从BarStore返回所有项目

或者只是回到类中提供的默认配置: / p>

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

var items = panel.getItems(); //从FooStore返回所有项目

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

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

//对于此面板,我们只想返回前10个项目
getItems:function(){
return this.store.getRange(0, 9);
},

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

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

var items = panel.getItems(); //返回FooStore的前10个项目



声明式vs命令式



将这一点与每次必须为股票类实例指定完整配置的必要方法进行比较:

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

//注意,我们不能使用this.callParent()这里
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',{
extends:'Ext.tree.TreePanel',//你使用子类

initComponent:function(){

//但是在这里,您将配置选项
//分配给
//实例化的*类实例*已经初始化一半已经
this.store ='TreeNodes';
...
}
});

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



假设在路上,您将需要在应用程序的其他地方重用您的MainTree类,但现在使用不同的存储或行为。使用上面的代码,您不能轻松实现,您将不得不创建另一个类并覆盖 initComponent 方法:

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

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

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


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.

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

Custom Ext.data.TreeStore implementation:

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

Custom Ext.tree.Panel implementation:

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

解决方案

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

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

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

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.

Instance configuration

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
    }
});

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.

Declarative classes

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();
    }
});

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.

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

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);
    }
});

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.

Subclassing pitfalls

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.

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&amp; Ext.data.TreeStore作为两个例子)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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