Javascript-форум (https://javascript.ru/forum/)
-   ExtJS (https://javascript.ru/forum/extjs/)
-   -   Загрузка контента в центральный фрейм по клику в tree.Panel (https://javascript.ru/forum/extjs/41092-zagruzka-kontenta-v-centralnyjj-frejjm-po-kliku-v-tree-panel.html)

sergeyv 30.08.2013 23:30

Загрузка контента в центральный фрейм по клику в tree.Panel
 
Здравствуйте,

Совсем недолго изучаю Javascript и в частности ExtJS и что-то у меня случился затык с тем, как загрузить контент в центральный фрейм по клику из меню tree.Panel.

Код TreeStore:
Ext.define('CM.store.MenuTreeStore', {
    extend: 'Ext.data.TreeStore',
    root: {
        expanded: true,
        children: [
            {
                // General section
                text: 'General',
                leaf: false,
                expanded: true,
                children: [
                    {
                        text: 'Partners',
                        leaf: true,
                        icon: 'images/group.png',
                        id: 'partnerslist'
                    },
                    {
                        text: 'Users',
                        leaf: true,
                        id: 'userslist'
                    }
                ]
            },
            {
                // Expenses
                text: 'Expenses',
                leaf: false,
                expanded: true,
                children: [
                    {
                        text: 'Serviss',
                        leaf: true,
                        id: 'servisslist'
                    }
                ]
            }
        ]
    }
});


Код навигации:
Ext.define('CM.view.Navigation', {
    extend: 'Ext.tree.Panel',
    alias: 'widget.appnavigation',
    width: 200,
    title: 'Menu',
    collapsible: true,
    collapseDirection: 'left',
    titleCollapse: true,
    store: 'MenuTreeStore',
    renderTo: Ext.getBody(),
    rootVisible: false,
    listeners:{
        itemclick: function(view,record) {
            if(record.isLeaf()) {
                console.log(record.data.id);
            } else if(record.isExpanded()) {
                record.collapse();
            } else {
                record.expand();
            }
        }
    }
});


Есть набор форм, примерно вот такого вида, которые предназначены для сбора разного рода данных, которые надо выводить в центральный фрейм border layout при клике в меню.
Ext.define('CM.view.user.List', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.userlist',
    xtype: 'userslist',
    allowDeselect: true,
    columnLines: true,
    forceFit: true,
    title: 'All Users',
    store: 'Users',

    initComponent: function () {

        this.tbar = [{
            text: 'Add User', action: 'create', icon: '/images/add.png'
        }];
        this.columns = [
            { header: 'Name', dataIndex: 'name', flex: 1 },
            { header: 'Email', dataIndex: 'email', flex: 1 },
            { header: 'Phone', dataIndex: 'phone', flex: 1 }
        ];
        this.addEvents('removeitem');
        this.actions = {
            removeitem: Ext.create('Ext.Action', {
                text: 'Remove User',
                handler: function () { this.fireEvent('removeitem', this.getSelected()) },
                scope: this
            })
        };
        var contextMenu = Ext.create('Ext.menu.Menu', {
            items: [
                this.actions.removeitem
            ]
        });
        this.on({
            itemcontextmenu: function (view, rec, node, index, e) {
                e.stopEvent();
                contextMenu.showAt(e.getXY());
                return false;
            }
        });
        this.callParent(arguments);
    },
    getSelected: function () {
        var sm = this.getSelectionModel();
        var rs = sm.getSelection();
        if (rs.length) {
            return rs[0];
        }
        return null;
    }
});


По клику в меню, я добился того, что у меня в консоли отображается id leof-ов из MenuTreeStore, только что с ним дальше делать, пока не понимаю. Может кто из гуру подтолкнёт меня в нужном направлении? Желательно реальным примером.

Заранее спасибо!

P.S. в качестве серверной части у меня RoR 4.0

С уважением,
Сергей

novikov 31.08.2013 00:27

Цитата:

Сообщение от sergeyv (Сообщение 270153)
Совсем недолго изучаю Javascript и в частности ExtJS...

впечатляет...

novikov 31.08.2013 01:18

Предлагаю поместить все формы в контейнер с layout: 'fit'. Все формы скрыты при помощи параметра hidden: true. Когда пользователь щёлкает по меню, контроллер находит нуждую форму и загружает в неё данные. Снабдите формы параметром itemId, когда будете размещать их в контейнере, чтобы их можно было потом найти. Контейнеру тоже дайте какой-нибудь itemId.

Ext.define('CM.controller.NavigationController', {
     extend: 'Ext.app.Controller',
     init: function() {
          this.listen({
               component: {
                      appnavigation: {
                            itemclick: this.onNavigationItemClick
                      }
               }
          });
     },
     onNavigationItemClick: function(treeview, record) {
               var formContainer = Ext.ComponentQuery.query('[itemId="form-container"]')[0];
               // скрываем все формы
               Ext.Array.each(formContainer.items, function(formItem) {
                    formItem.hide();
               });
               // находим нужную форму
               var activeForm = Ext.ComponentQuery.query('[itemId="' + record.get('id') + '"]')[0];
               // показываем эту форму
               activeForm.show();
               // загружаем в активную форму данные (например, с сервера)
               if (activeForm.is('grid')) {
                       activeForm.getStore().load({
                              params: {

                              }
                       });
               }
     }
}


Хотя правильнее было бы иметь для каждой формы свой контроллер с обработчиком события, приходящего от контроллера навигации. Тогда не потребуется такое ветвление if (activeForm.is('grid')) {}.

sergeyv 31.08.2013 12:15

Здравствуйте, Александр.

Спасибо, буду пробовать, но вот сразу вопрос, если таких гридов у меня будет на 2-3, а 20, причём со всякими связанными гридами и прочими динамически вычесляемыми полями и функциями, то, я подозреваю, что такая реализация может очень сильно повлиять на перформанс и всё это хозяйство будет безбожно тормозить. Или я не прав?

Заранее спасибо за помощь.

С уважением,
Сергей

novikov 31.08.2013 15:08

Предполагяю, что на 20 формах тормозов не будет, если, конечно, не загружать таблицы из 50000 ячеек (и то только во время отрисовки).

Если вы считаете, что динамическая генерация компонентов ускорит работу приложения, можно запускать Ext.create('класс.Компонента') только при клике по соответствующему пункту меню и добавлять его в контейнер при помощи add() или renderTo.

У такого подхода есть свои недостатки. Я стремлюсь дистанцироваться от него как можно дальше. В том числе и из-за того, что отсутствующие компоненты не слышат события, на которые им следовало бы отреагировать.

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

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

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

Вместо layout: 'fit', можно попробывать layout: 'card', но отложенная отрисовка компонентов может вызвать затруднения на перовом этапе, поэтому не советую.

sergeyv 02.09.2013 21:39

Александр, попробовал ваше решение, даже разобрался вроде как работать должно, но вот что-то не так пошло и в консоли я упираюсь в ошибку JS - Uncaught TypeError: Cannot call method 'hide' of undefined

При помощи console.log() вывел formContainer и вижу тут странный id: "contentPanel-1015". По вашему совету, добавил все формы в один контейнер и скрыл их. Может косяк какой-то из-за того, что я в качестве контейнера использую Panel?

Ext.define('CM.view.ContentPanel', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.contentPanel',
    itemId: 'content',

    requires: [
        'CM.view.user.List',
        'CM.view.partner.List'
    ],

    title: '',
    layout: 'fit',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'partnerlist',
                    hidden: true,
                    itemId: 'partners'
                },
                {
                    xtype: 'userlist',
                    hidden: true,
                    itemId: 'users'
                }
            ]
        });

        me.callParent(arguments);
    }
});

и мой контроллер выглядит вот так:
Ext.define('CM.controller.Navigation', {
        extend: 'Ext.app.Controller',
        itemId: 'navigation',
        init: function() {
            this.listen({
                component: {
                    appnavigation: {
                        itemclick: this.onNavigationItemClick
                }
            }
        });
        },
        onNavigationItemClick: function(treeview, record) {
            var formContainer = Ext.ComponentQuery.query('[itemId="content"]')[0];
            // скрываем все формы
            console.log(formContainer);
            Ext.Array.each(formContainer.items, function(formItem) {
                formItem.hide();
            });
            // находим нужную форму
            var activeForm = Ext.ComponentQuery.query('[itemId="' + record.get('id') + '"]')[0];
            // показываем эту форму
            activeForm.show();
            // загружаем в активную форму данные (например, с сервера)
//            if (activeForm.is('grid')) {
//                activeForm.getStore().load({
//                        params: {
//                        }
//                });
//            }
        }
});


Может подскажете, что я не так понял и где путаюсь?

Заранее спасибо,
Сергей

novikov 03.09.2013 11:51

Сергей, id у formContainer, на мой взгляд, вполне нормальный. Он создался автоматически, поскольку он не был задан через параметр id.

Проблема в том, что я по ошибке неправильно указал способ перебора элементов контейнера. items не является массивом JavaScript, а экземпляром класса Ext.util.MixedCollection. Для перебора нужно использовать метод each().

http://docs.sencha.com/extjs/4.2.1/#...on-method-each

formContainer.items.each(function(formItem) {
    formItem.hide();
});

sergeyv 03.09.2013 12:27

Александр, Вы человек!

Спасибо огромное, всё заработало :) Пошёл штудировать матчасть

С уважением,
Сергей

sergeyv 03.09.2013 20:13

Здравствуйте,

опять затык :blink:

Решил по образу и подобию добавить ещё один пунктик меню, но на этот раз это должна быть панель, с двумя связанными гридами.

Методом проб и ошибок решил что ContentPanel стоит сделать Container'ом, а не Panel-ю
Ext.define('CM.view.ContentPanel', {
    extend: 'Ext.container.Container',
    //alias: 'widget.contentPanel',
    xtype: 'contentPanel',
    itemId: 'content',

    layout: 'fit',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'partnerlist',
                    hidden: true,
                    itemId: 'partners'
                },
                {
                    xtype: 'userlist',
                    hidden: true,
                    itemId: 'users'
                },
                {
                    xtype: 'partnercontactslist',
                    hidden: true,
                    itemId: 'partnercontactslist'
                }
            ]
        });

        me.callParent(arguments);
    }
});


и панель с гридами у меня вот такая:

Ext.define('CM.view.partnercontact.List', {
    extend: 'Ext.panel.Panel',
    xtype: 'partnercontactlist',
    forceFit: true,
    title: '',

    layout: {
        align: 'stretch',
        type: 'vbox'
    },

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'partnerspanel'
                },
                {
                    xtype: 'partnercontacts'
                }
            ]
        });

        me.callParent(arguments);
    }
});


Ну и соответственно гриды. Главный:
Ext.define('CM.view.partnercontact.Partners', {
    extend: 'Ext.grid.Panel',
    xtype: 'partnerspanel',

    title: 'Partners',
    store: 'Partners',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            columns: [
                {
                    xtype: 'gridcolumn',
                    header: 'Name',
                    dataIndex: 'name',
                    flex: 1
                },
                {
                    xtype: 'gridcolumn',
                    header: 'Phone',
                    dataIndex: 'phone',
                    flex: 1
                },
                {
                    xtype: 'gridcolumn',
                    header: 'Fax',
                    dataIndex: 'fax',
                    flex: 1
                },
                {
                    xtype: 'gridcolumn',
                    header: 'Address',
                    dataIndex: 'address',
                    flex: 1
                }
            ]
        });

        me.callParent(arguments);
    }

});


и связанный
Ext.define('CM.view.partnercontact.Contacts', {
    extend: 'Ext.grid.Panel',
    xtype: 'partnercontacts',

    title: 'Related contacts',
    store: 'Contacts',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            columns: [
                {
                    xtype: 'gridcolumn',
                    header: 'Name',
                    dataIndex: 'name',
                    flex: 1
                },
                {
                    xtype: 'gridcolumn',
                    header: 'Phone',
                    dataIndex: 'phone',
                    flex: 1
                },
                {
                    xtype: 'gridcolumn',
                    header: 'Email',
                    dataIndex: 'email',
                    flex: 1
                },
                {
                    xtype: 'numbercolumn',
                    header: 'Partner ID',
                    dataIndex: 'partner_id',
                    flex: 0
                }
            ]
        });

        me.callParent(arguments);
    }

});


в MenuTreeStore вот такое добавил:

{
                        text: 'Partners/Contacts',
                        leaf: true,
                        icon: 'images/group_link.png',
                        id: 'partnercontactslist'
                    }


Но почему-то вообще всё ломается. Загружается только дерево навигации, ну и вообщем всё летит в тар тарары.

Комментирую в ContentPanel новую форму, всё возвращается на места и работает.

//                {
//                    xtype: 'partnercontactslist',
//                    hidden: true,
//                    itemId: 'partnercontactslist'
//                }


Отдельный котнроллер я под эту форму не создавал, потому как вообщем-то и так должно работать, вроде :(

Пните пожалуйста в нужном направлении.

Заранее спасибо за помощь.

С уважением,
Сергей

sergeyv 06.09.2013 20:40

Проблема решилась.

Создал panel с
layout: {
        align: 'stretch',
        type: 'vbox'
    },
    title: '',


И всё там аккуратненько уложил. Завелось и зажило. Спасибо!


Часовой пояс GMT +3, время: 14:49.