Поиск по этому блогу

понедельник, 19 сентября 2011 г.

ExtJS 3.1.0. Дерево и Tab panel с Grid'ом



Пример. Дерево, по клику на ноде создается tab с гридой.
За отображение и заполнение дерева отвечают:
    mainwin/spr-main-win.js
    mainwin/spr-main-tree.php
За отображение гриды
    mainwin/grid-class-vdoc.js
    mainwin/model-vdoc.php
Интерфейс   состоит из одного окна (window), в котором на западе растет дерево, в центре tab panel. На дереве поставлен listener, который обрабатывает событие on click - добавляется новая tab panel.


За добавление панелей отвечает функция
function addTab(index, title) {  
В зависимости от наименования ноды (свойство title) выбирается тип гриды, которую мы создали и зарегистрировали (пример код ниже grid-class-vdoc.js )
    if  (title == 'Тип документа') {gridxtype = 'tdocgrid'};
    if  (title == 'Вид документа') {gridxtype = 'vdocgrid'};
Проверяем, что такая вкладка отсутствует
       if  (gridxtype !='')  {
           if (!tabs.findById(title)) {
Добавляем новую панель
            var tab = tabs.add({
                id: title,
                title: title,
                iconCls: 'icon-tab',
                bodyStyle: 'padding: 5px',
                xtype: gridxtype, //'tdocgrid', // тип определен в grid-class-tdoc
                closable: true
            }); // eo tabs add
            //console.info(tab.id);
            tab.show();
        } else {
            tabs.setActiveTab(tabs.findById(title));
        } // eo find уже есть такая вкладка
        }  // вообще такой тип вкладки описан    
}; // eo funct addTab
Для того, чтобы на вкладке появился наш грид, мы предварительно создаем классы-расширения, регистрируем их как новый тип, а потом лениво (lazy) используем
Т.е. создаем, регистрируем
grid-class-vdoc.js
Vdoc.Grid = Ext.extend(Ext.grid.GridPanel, { ... // создаем новое расширение
Ext.reg('vdocgrid', Vdoc.Grid); // регистрируем наш класс для дальнейшего использования
Используем зарегистрированный тип
spr-main-win.js
var tab = tabs.add({...
                xtype: 'vdocgrid', ...


listing mainwin/spr-main-win.js
/** 
 * Дерево справочников с табами и гридами
 */
Ext.onReady(function(){
           
        // загружает дерево
        var treeLoader = new Ext.tree.TreeLoader({
            dataUrl:'spr-main-tree.php' // серверный код, который будет строить дерево
        });
        // корневая ветка
        var rootNode = new Ext.tree.AsyncTreeNode({
            text: 'Справочники'
            , draggable: false
            , id: '1' // id по умолчанию       
        });
       
        // обычная модель выбора       
        var smodel = new Ext.tree.DefaultSelectionModel;       

        // на западе будет расти дерево
        var tree = new Ext.tree.TreePanel({
            title: 'Список',
            region: 'west',
            split: true,
            width: 200,
            collapsible: true,
            margins:'3 0 3 3',
            cmargins:'3 3 3 3',
            //renderTo:'treecontainer',
            loader: treeLoader,
            root: rootNode,
            rootVisible: true,
            selModel: smodel           
        });
       
       // В центре - tab panel
        var tabs = new Ext.TabPanel({
            region: 'center',
            margins:'3 3 3 0',
            activeTab: 0,
            defaults:{autoScroll:true}           
        });
    // главное окно   
    var win = new Ext.Window({
            title: 'Справочники',
            closable:true,
            collapsible: true,
            maximizable: true,           
            width:1000,
            height:350,
            //border:false,
            plain:true,
            layout: 'border',

            items: [tree, tabs] // дерево и панель закладок
        });   

//*************** функции **************************
function addTab(index, title) {  
    gridxtype = '';
    if  (title == 'Тип документа') {gridxtype = 'tdocgrid'};
    if  (title == 'Вид документа') {gridxtype = 'vdocgrid'};
    //console.info(gridxtype);
           // проверяем что такая вкладка отсутствует и ее тип "Тип документа"
       if  (gridxtype !='')  {
           if (!tabs.findById(title)) {
            var tab = tabs.add({
                id: title,
                title: title,
                iconCls: 'icon-tab',
                bodyStyle: 'padding: 5px',
                xtype: gridxtype, //'tdocgrid', // тип определен в grid-class-Xdoc
                closable: true
            }); // eo tabs add
            //console.info(tab.id);
            tab.show();
        } else {
            tabs.setActiveTab(tabs.findById(title));
        } // eo find уже есть такая вкладка
        }  // вообще такой тип вкладки описан    
}; // eo funct addTab


// ************ события ****************************
tree.on('click', function(node) {   
    addTab(0,node.attributes.text);
});

// выбор ветки
//tree.selModel.on('selectionchange', function(selModel, node) {
//    Ext.Msg.alert(node.attributes.text);
//});   
       
    win.show(this);   
});

/**
 * Класс-расширение для грида "вид документов"
    mainwin/grid-class-vdoc.js
 */
// namespace
Ext.ns('Vdoc');

Ext.BLANK_IMAGE_URL = '../../lib/extjs/resources/images/default/s.gif';

Vdoc.Grid = Ext.extend(Ext.grid.GridPanel, {
    initComponent: function(){
        var config = {
            store: new Ext.data.JsonStore({
                id: 'ID',
                totalProperty: 'total',
                successProperty: 'success',               
                root: 'tablerow',
                url: 'model-vdoc.php', // серверный код, который будет загружать данные в грид
                autoLoad: false,
                fields: [
             {name: 'ID', type: 'string', mapping: 'ID'}
            ,{name: 'vdoc', type: 'string', mapping: 'VDOC'}
                ,{name: 'namedoc', type: 'string', mapping: 'NAMEDOC'}                          
            ]
            }),
            columns: [
        {
            id: 'ID',
            header: 'PK',
            dataIndex: 'ID',
            width: 70,
        hidden : true            
        }, {
                header: 'Вид документа', dataIndex: 'namedoc',
                width: 200, hidden : false
            }, {
                header: 'Вид', dataIndex: 'vdoc',
                width: 90, hidden : false
            }
        ],
            viewConfig: {
                forceFit: true
            },
            loadMask: true           
        }; // eo config object
        // apply config
        Ext.apply(this, Ext.apply(this.initialConfig, config));
        // нижняя панель с листалкой PagingToolbar
        this.bbar = new Ext.PagingToolbar({
            store: this.store,
            displayInfo: true,
            pageSize: 10
        });
       
        // родитель
        Vdoc.Grid.superclass.initComponent.apply(this, arguments);
       
        // грузим store
        this.on({
            afterlayout: {
                scope: this,
                single: true,
                fn: function(){
                // !!! Здесь грузим хранилище store
                    this.store.load({
                        params: {start: 0,limit: 10}
                    });
                }
            }
        });
       
    } // eo function initComponent
    ,
    afterRender: function(){
        Vdoc.Grid.superclass.afterRender.apply(this, arguments);
        ...
    } // eo function afterRender
});
// регистрируем наш класс для дальнейшего использования
Ext.reg('vdocgrid', Vdoc.Grid);
   
//********************* Серверный код ***********************//
Код, который строит дерево
Данные для дерева сервер отправляет в таком виде
[{"id":2,"text":"Тип документа","leaf":true},{"id":3,"text":"Вид документа","leaf":true},{"id":4,"text":"КБК","leaf":true}]
<?php
/* Дерево - меню для отображения наименований справочников */
    $host="127.0.0.1:C:/Apache/www/BD/PUBOB.FDB";
    $username="SYSDBA";
    $password="masterkey";
    $errmsg = '';
$stmt = 'select ID, TEXT from TREE_SPRAV where PARENTID =';   
$parent_id = isset($_POST['node']) ? $_POST['node'] : 1;
///************** соединение **************************
$dbh = @ibase_connect($host, $username, $password);
if ($dbh == FALSE)
{
    $errmsg = 'Ошибка соединения '. ibase_errormsg();
    //echo $errmsg;
}

$out = GetTree($dbh, $stmt, $parent_id);
ibase_close($dbh);
echo $out;

// получить дерево начиная с ветки $parent_id
function GetTree($dbh, $stmt, $parent_id) {
    $stmt_node = $stmt. $parent_id;
    //$stmt = 'select ID, TEXT from TREE_SPRAV where PARENTID =1';
    //echo $stmt;
    $dst = @ibase_query($dbh, $stmt_node); // выполняем запрос   
    if ($dst == FALSE)
    {
    $errmsg = 'Ошибка выполнения '. ibase_errmsg();
    echo $errmsg;
    }
    $json_dst = '';
   
while ($row = ibase_fetch_object($dst)) {
    //echo $row->ID . "\n";
    $node['id']= $row->ID;
    $node['text']= $row->TEXT;
    // проверяем есть ли у этой ветки потомки
    $stmt_child = $stmt. $row->ID;   
    $dst_child = ibase_query($dbh, $stmt_child);
    $row_child = ibase_fetch_assoc($dst_child);
// если потомков нет - это лист (leaf)
    $node['leaf'] = count($row_child) > 1 ? false : true;
       
    $result[] = $node; // добавляем ноду к массиву результат
   

} // eo while
   ibase_free_result($dst);
   return json_encode($result);

}; // eo funct   
?>

Таблица с данными
CREATE TABLE TREE_SPRAV (
    ID        INTEGER NOT NULL,
    TEXT      VARCHAR(150),
    PARENTID  INTEGER
);


Часть серверного кода, отвечающего за заполнение гриды
В таком виде сервер отправляет данные
$out ='{ "success": true,"total": 9, "tablerow":[ {"ID":1,"TDOC":1,"NAMETDOC":"name tdoc 1"} , {"ID":2,"TDOC":2,"NAMETDOC":"name 2"}]}';

$dbh - соединение ($dbh = ibase_connect($host, $username, $password);)
$stmt - Запрос для выбора данных
$stmt =     'select '
   .' ID, VDOC, NAMEDOC from SPR_VDOC '  
   . ' [ORDER]  [ROWS] ';
$stmtcount - Запрос для подсчета количества записей
$stmtcount = "select count(*) as rccount from SPR_VDOC ";

Вариант, который проще для восприятия.
function getRecords($dbh, $stmt, $stmtcount, $sort, $dir, $start, $limit) {
    // всего записей
    $record_count = getRecordCount($dbh, $stmtcount);   
    // строим строчку для select rows 1 to 10
    $fetch_limit = "";
    // проверка сортировки и dir
    if ((!strlen($sort) == 0) && (in_array(strtoupper($sort), $fields_array))
    && (in_array($dir, array('ASC', 'DESC'))))   
    {$sort = ' order by '.$sort .' '. $dir;}
    // page с.. по..
    if (($start >= 0) && ($limit>0)){
        if ($start ==0) {$start=1;}
        $limit = $start + $limit;
        $fetch_limit = ' rows ' . ($start) . ' to '.$limit ;
        }
    $stmt = str_replace('[ORDER]',$sort, $stmt);
    $stmt = str_replace('[ROWS]',$fetch_limit, $stmt);

    $dst = @ibase_query($dbh, $stmt); // выполняем запрос   
    if ($dst == FALSE)
    {
    $errmsg = 'Ошибка выполнения '. ibase_errmsg();
    }
    $json_dst = '';
   
    while($row = ibase_fetch_assoc($dst)) {   
        // собираем строки            
        if ($json_dst == '') {
            $json_dst = json_encode($row);
        } else {
            $json_dst .= ' , '.json_encode($row);
        }
    } // eo while       
    // вот такая строка у нас должна получиться
    $json_dst = '{"success": true,"total": '.$record_count.', "tablerow":[ '.$json_dst.']}';
       
    ibase_free_result($dst);
    return $json_dst; // возвращаем данные в формате json
}

А это часть класса, который делает тоже самое, что и  только код немного изменен
function getRecords($stmt, $stmtcount, $sort, $dir, $start, $limit, $filters){
    // получить количество записей
    $record_count = self::getRecordCount($stmtcount);
   
    // строим строчку для select rows 1 to 10
    $fetch_limit = "";
    // проверка сортировки и dir
    if ((!strlen($sort) == 0) && (in_array(strtoupper($sort), $fields_array))
    && (in_array($dir, array('ASC', 'DESC'))))   
    {$sort = ' order by '.$sort .' '. $dir;}
    // page с.. по..
    if (($start >= 0) && ($limit>0)){
        if ($start ==0) {$start=1;}
        $limit = $start + $limit;
        $fetch_limit = ' rows ' . ($start) . ' to '.$limit ;
        }
           
    $stmt = str_replace('[ORDER]',$sort, $stmt);
    $stmt = str_replace('[ROWS]',$fetch_limit, $stmt);   
    $stmt = str_replace('[WHERE]',$restrict, $stmt);

    $dst = @ibase_query(self::$dbh, $stmt); // выполняем запрос   
    if ($dst == FALSE)
    {
    $errmsg = 'Ошибка выполнения '. ibase_errmsg();
    }
    $json_dst = '';   
    $rows = array();       
    while($row = ibase_fetch_assoc($dst)) {   
        $rows = array_merge($rows, array($row));       
    } // eo while
   
    $rows = array('tablerow' => $rows);
    $meta = array('metaData' => $meta);
   
    $arr_out = $meta + array('success' => "true", "total" => $record_count) + $rows;
    ibase_free_result($dst);
   
    return json_encode($arr_out); // возвращаем данные в формате json
   
} // eo function getRecordsWithMetadata