30.08.2013, 23:30
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Загрузка контента в центральный фрейм по клику в 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
С уважением,
Сергей
|
|
31.08.2013, 00:27
|
Профессор
|
|
Регистрация: 19.11.2012
Сообщений: 178
|
|
Сообщение от sergeyv
|
Совсем недолго изучаю Javascript и в частности ExtJS...
|
впечатляет...
|
|
31.08.2013, 01:18
|
Профессор
|
|
Регистрация: 19.11.2012
Сообщений: 178
|
|
Предлагаю поместить все формы в контейнер с 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')) {}.
|
|
31.08.2013, 12:15
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Здравствуйте, Александр.
Спасибо, буду пробовать, но вот сразу вопрос, если таких гридов у меня будет на 2-3, а 20, причём со всякими связанными гридами и прочими динамически вычесляемыми полями и функциями, то, я подозреваю, что такая реализация может очень сильно повлиять на перформанс и всё это хозяйство будет безбожно тормозить. Или я не прав?
Заранее спасибо за помощь.
С уважением,
Сергей
|
|
31.08.2013, 15:08
|
Профессор
|
|
Регистрация: 19.11.2012
Сообщений: 178
|
|
Предполагяю, что на 20 формах тормозов не будет, если, конечно, не загружать таблицы из 50000 ячеек (и то только во время отрисовки).
Если вы считаете, что динамическая генерация компонентов ускорит работу приложения, можно запускать Ext.create('класс.Компонента') только при клике по соответствующему пункту меню и добавлять его в контейнер при помощи add() или renderTo.
У такого подхода есть свои недостатки. Я стремлюсь дистанцироваться от него как можно дальше. В том числе и из-за того, что отсутствующие компоненты не слышат события, на которые им следовало бы отреагировать.
Если вы опасаетесь, что поиск компонентов по строковым селекторам может стать причиной медленной работы приложения, то это вряд ли, поскольку в эту систему встроен механизм кэширования.
Если вас беспокоит большое количество файлов классов, которые нужно подгрузить перед запуском приложения, то для боевой эксплуатации приложение собирается в один файл и минифицируется.
Если ожидаете, что с сервера нужно будет подгрузить слишком много данных для всевозможных форм и это вызовет задержку, то просто не грузите все данные с сервера сразу через autoLoad. Вместо этого подгружайте данные в обработчиках событий beforerender и т.п.
Вместо layout: 'fit', можно попробывать layout: 'card', но отложенная отрисовка компонентов может вызвать затруднения на перовом этапе, поэтому не советую.
|
|
02.09.2013, 21:39
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Александр, попробовал ваше решение, даже разобрался вроде как работать должно, но вот что-то не так пошло и в консоли я упираюсь в ошибку 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: {
// }
// });
// }
}
});
Может подскажете, что я не так понял и где путаюсь?
Заранее спасибо,
Сергей
|
|
03.09.2013, 11:51
|
Профессор
|
|
Регистрация: 19.11.2012
Сообщений: 178
|
|
Сергей, 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();
});
|
|
03.09.2013, 12:27
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Александр, Вы человек!
Спасибо огромное, всё заработало Пошёл штудировать матчасть
С уважением,
Сергей
|
|
03.09.2013, 20:13
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Здравствуйте,
опять затык
Решил по образу и подобию добавить ещё один пунктик меню, но на этот раз это должна быть панель, с двумя связанными гридами.
Методом проб и ошибок решил что 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'
// }
Отдельный котнроллер я под эту форму не создавал, потому как вообщем-то и так должно работать, вроде
Пните пожалуйста в нужном направлении.
Заранее спасибо за помощь.
С уважением,
Сергей
|
|
06.09.2013, 20:40
|
Новичок на форуме
|
|
Регистрация: 30.08.2013
Сообщений: 8
|
|
Проблема решилась.
Создал panel с
layout: {
align: 'stretch',
type: 'vbox'
},
title: '',
И всё там аккуратненько уложил. Завелось и зажило. Спасибо!
|
|
|
|