Шаги по переопределению функциональности стандартного компонента Sencha ExtJS (Ext.дерево.Панель И Внутр.данные.TreeStore как два примера)

предположим, я расширяю стандартный виджет/компонент Sencha ExtJS 4, и я нашел кучу вещей, которые не работают так, как я хочу, или, возможно, они просто сломаны, и Sencha еще не удосужился исправить проблемы с компонентом. Я просто собираюсь использовать Sencha ExtJS Ext.дерево.Панель и внутр.дерево.Хранить как два примера компонентов. Каковы наиболее основные шаги для переопределения конструктора, конфигураций, свойств, методов и событий, чтобы я мог найти и исправить проблемы с этот компонент без изменения основного файла ExtJS 4 framework JS, который я сейчас использую?

Я понимаю, что иногда в фреймворке так много функциональности, что можно где-то упустить конфигурацию и не понять, что они могут исправить проблему со стандартной реализацией. И это то, что можно исправить с большим опытом работы с фреймворком. Отложив это в сторону, каковы будут эти самые основные шаги?

Предположим, мы начнем с этих двух реализации и начать с самых основ.

FYI: я получил основные функции этих двух компонентов, работающих без особых усилий, действительно используя Ext.Прямой стек на стороне сервера, и я мог бы объяснить все проблемы, совместимые с кросс-браузером, с Sencha ExtJS Ext.дерево.Компонент панели с IE, Mozilla Firefox и Google Chrome, но я, вероятно, потратил бы слишком много времени, задавая эти другие вопросы. И я не говорю, что IE сначала должен быть стереотипным, потому что все это браузеры имеют свои проблемы с внутр.дерево.Компонент панели. Я лучше научусь здесь рыбачить, чтобы ловить свою рыбу. Как только я лучше пойму эти классы, связанные с деревом, я задам более конкретные вопросы.

http://docs.sencha.com/extjs/4.2.1/#!/api / Ext.данные.Древостой

Пользовательские Ext.данные.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.дерево.Панель

Пользовательские Ext.дерево.Реализация панели:

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

2 ответов


обзор

существует три способа увеличения поведения классов акций в Ext JS 4.x без изменения источника фреймворка: подклассы, переопределение классов и конфигурация экземпляра.

наследование

подклассы-это то, что вы делаете, когда вам нужно создать пользовательский компонент, специально для вашего приложения. Это на самом деле то, что вы делаете в коде выше: вы берете компонент запаса, изменяя его поведение в соответствии с вашими потребностями, и используя его в качестве нового компонента. Важный момент заключается в том, что при подклассе вы не изменяете поведение компонента запаса, поэтому вы можете использовать как пользовательские, так и фондовые компоненты.

переопределение

переопределение-это еще один подход, который изменит поведение класса stock:

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, как на складе, так и на заказ. Этот подход в основном используется для патчи и исправления; его можно использовать для добавления новых функций в компоненты запаса, но вам будет сложнее поддерживать его в будущем.

конфигурации экземпляра

тем не менее, самый популярный подход до сих пор заключается в создании экземпляров классов акций и изменении поведения экземпляров параметры config и переопределение методов:

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 и по-прежнему активно используется. Я не рекомендую этот подход для new 4.х приложений, потому что это не позволит вам распределить ваш код правильно и труднее поддерживать в долгосрочной перспективе.

декларативный классы

еще одно преимущество перехода к подклассу заключается в том, что он позволяет сохранить декларативный код вместо императивного:

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 config для экземпляр:

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

декларативный vs императивный

сравните это с императивным подходом, в котором вам придется каждый раз указывать полную конфигурацию для экземпляра класса stock:

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

сравните это с переопределением конфигурации экземпляра выше. Не только декларативный подход легче писать и поддерживать, но и бесконечно более проверяемым.


переопределите функции, которые, по вашему мнению, работают неправильно или так, как вы хотите, чтобы он работал

пример

Ext.define('MyApp.store.TreeGridStore', {
extend: 'Ext.data.TreeStore',
getTotalCount : function() {
    if(!this.proxy.reader.rawData) return 0;
    this.totalCount = this.proxy.reader.rawData.recordCount;
    return this.totalCount;
},  
....

в приведенном выше примере я хочу, чтобы функция getTotalCount вычисляла количество по-разному, поэтому я расширил treestore и переопределил метод.