Kroki w celu nadpisania funkcji standardowego komponentu Sencha ExtJS (Ext.drzewo.Panel & Ext.data.TreeStore jako dwa przykłady)

Załóżmy, że rozszerzam standardowy widżet/komponent Sencha ExtJS 4 i znalazłem kilka rzeczy, które nie działają tak, jak chcę, a może są po prostu zepsute i Sencha jeszcze nie naprawił problemów z komponentem. Użyję tylko Sencha ExtJS Ext.drzewo.Panel i wew.drzewo.Przechowuj jako dwa przykładowe komponenty. Jakie są najbardziej podstawowe kroki, aby nadpisać konstruktor, configs, właściwości, metody i zdarzenia, abym mógł znaleźć i naprawić problemy z ten komponent bez modyfikowania pliku JS rdzenia ExtJS 4 framework, którego obecnie używam?

Zdaję sobie sprawę, że czasami jest tak dużo funkcjonalności w frameworku, że można przeoczyć gdzieś konfigurację i nie zdawać sobie sprawy, że mogą rozwiązać problem ze standardową implementacją. I to jest coś, co można poprawić przy większym doświadczeniu z frameworkiem. Odkładając to na bok, jakie byłyby te najbardziej podstawowe kroki?

Załóżmy, że zaczniemy od tych dwóch implementacji i zacznij od samych podstaw.

FYI: mam podstawowe cechy tych dwóch komponentów, które działają bez zbytniego wysiłku, naprawdę używając Ext.Bezpośredni stos po stronie serwera i mógłbym wyjaśnić wszystkie problemy z kompatybilnością między przeglądarkami z Sencha ExtJS Ext.drzewo.Komponent panelu z IE, Mozilla Firefox i Google Chrome, ale pewnie za dużo czasu poświęciłbym na zadawanie tych innych pytań. I nie mówię, że IE jako pierwszy jest stereotypowy, bo wszystkie te przeglądarki mają swoje problemy z Ext.drzewo.Komponent panelu. Wolałbym nauczyć się tu łowić ryby, żebym mógł łowić własną rybę. Kiedy lepiej zrozumiem te klasy związane z drzewem, zadam bardziej szczegółowe pytania.

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

Custom Ext.data.Implementacja 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

Custom Ext.drzewo.Wykonanie panelu:

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) {
        }
    }
});
Author: tereško, 2013-08-15

2 answers

Przegląd

Istnieją trzy sposoby zwiększenia zachowania klas stock w Ext JS 4.x bez zmiany źródła frameworka: podklasowanie, nadpisywanie klas i konfiguracja instancji.

Podkategoria

Podklasowanie jest tym, co robisz, gdy musisz utworzyć niestandardowy komponent dostosowany do Twojej aplikacji. To właśnie robisz w powyższym kodzie: bierzesz komponent magazynowy, zmieniasz jego zachowanie zgodnie z Twoimi potrzebami i używasz go jako nowego komponent. Ważne jest to, że poprzez podklasowanie nie zmieniasz zachowania komponentu stock, dzięki czemu możesz używać zarówno komponentów niestandardowych, jak i stock.

Overriding

Nadpisywanie jest kolejnym podejściem, które zmieni zachowanie klasy 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() {
        ...
    }
});

W ten sposób możesz zastosować zmiany, które będą miały wpływ na wszystkie instancje panelu drzewa, zarówno stock, jak i custom. To podejście jest najczęściej używane do łatek i poprawek; może być używane do dodawania nowe funkcje komponentów magazynowych, ale trudniej będzie utrzymać je na drodze.

Konfiguracja instancji

To powiedziawszy, najpopularniejszym do tej pory podejściem jest tworzenie instancji klas stock i zmiana zachowania instancji poprzez ustawienie opcji konfiguracyjnych i nadrzędnych metod:

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

Ten sposób robienia rzeczy został spopularyzowany we wcześniejszych wersjach Ext JS i jest nadal mocno używany. Nie polecam tego podejścia do nowych 4.aplikacje x, ponieważ nie pozwala to na poprawną modularyzację kodu i jest trudniejsze do utrzymania na dłuższą metę.

Klasy deklaratywne

Inną korzyścią płynącą z podklasowania jest to, że pozwala zachować kod deklaratywny zamiast imperatywnego:]}
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();
    }
});

Powyższa deklaracja klasy jest współdzielona przez wszystkie instancje MyApp.view.Panel, w tym zarówno opcję konfiguracji store, jak i nadpisanie metody initComponent, ale gdy tworzysz instancję tej klasy lub jej podklasy, initComponent metoda będzie działać na dowolnej konfiguracji bieżącej dla danej klasy .

Więc gdy używasz takiej klasy, będziesz miał do wyboru albo nadpisanie store config dla instancji :

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

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

Lub po prostu wraca do domyślnej konfiguracji podanej przez klasę:

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

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

Można również podklasować, nadpisując część konfiguracji lub zachowania, ale nie wszystko:

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

Deklaratywny vs Imperatyw

Porównaj to z imperatywnym podejściem, w którym będziesz musiał każdorazowo określać pełną konfigurację dla instancji klasy 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);
    }
});

Największą wadą powyższego kodu jest to, że wszystko dzieje się z instancją klasy, gdy jest już W połowie zainicjalizowaną (initcomponent jest wywoływany przez konstruktor). Nie można uogólnić tego podejścia i nie można łatwo sprawić, by instancje dzieliły większość zachowań, ale różniły się szczegóły-będziesz musiał powtórzyć kod dla każdej instancji.

Podklasyczne pułapki

To prowadzi nas do najczęstszego błędu, który ludzie popełniają również z podklasowaniem: pójście tylko w połowie drogi. Jeśli spojrzysz na powyższy kod, możesz zauważyć ten dokładny błąd:

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

Porównaj swój kod z powyższym przykładem deklaratywnym. Różnica polega na tym, że w twojej klasie konfiguracja dzieje się w czasie tworzenia instancji, podczas gdy w przykładzie dzieje się w czas deklaracji klas .

Załóżmy, że w przyszłości będziesz musiał ponownie użyć swojej klasy MainTree gdzie indziej w aplikacji, ale teraz z innym sklepem lub zachowaniem. Z powyższym kodem nie możesz tego zrobić łatwo i będziesz musiał utworzyć inną klasę i nadpisać metodę initComponent:

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

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

Porównaj to z nadpisaniem konfiguracji instancji powyżej. Nie tylko deklaratywne podejście jest łatwiejsze do napisania i utrzymania, ale jest nieskończenie bardziej testowalne.

 34
Author: Alex Tokarev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 20:53:15

Nadpisuj funkcje, które Twoim zdaniem nie działają poprawnie lub w sposób, w jaki chcesz, aby działały

Przykład

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;
},  
....

W powyższym przykładzie chcę, aby funkcja getTotalCount różnie obliczała liczbę, więc rozszerzyłem treestore i nadpisałem metodę.

 0
Author: objectone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 17:35:14