17.05.2012, 13:07
|
Новичок на форуме
|
|
Регистрация: 17.05.2012
Сообщений: 7
|
|
Как правильно спроектировать приложение?
Добрый день, коллеги!
Я проектирую простое приложение на основе Layout Browser
http://dev.sencha.com/deploy/ext-4.1...t-browser.html
В системе есть сущности: категории, товары, заказы.
Общий код для создания лайаута находится в отдельном файле и при клике на ветку дерева загружает класс
var m = Ext.create('Catalog.modules.имя_класса');
m.getIndexPanel()
Каждый модуль имеет такую структуру:
Ext.define('Catalog.modules.category', {
getStore: function() {
var me = this;
if (me.store === undefined) {
Ext.define('category', {
extend: 'Ext.data.Model',
fields: ['id', 'name', 'code']
});
me.store = Ext.create('Ext.data.Store', {
model: 'category',
proxy: {
type: 'rest',
url : '/catalog/products/',
reader: {
type: 'json',
root: 'items'
}
},
autoLoad: true,
autoSync: true
});
}
return me.store;
},
getIndexPanel: function() {
var me = this;
me.getStore();
var rowEditing = Ext.create('Ext.ux.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false
});
me.grid = Ext.create('Ext.grid.Panel', {
title: 'Категории',
store: me.store,
height: 700,
forceFit: true,
columns: [
{
text: 'Название категории',
dataIndex: 'name',
sortable : true,
editor: {
allowBlank: false
}
},
{
text: 'Код',
dataIndex: 'code',
editor: {
allowBlank: false
}
}
],
viewConfig: {
stripeRows: true
},
tbar: [
{
text: 'Добавить',
iconCls: 'add-item',
handler: function() {
if (rowEditing.editing) return false;
var item = Ext.create('category', {
});
me.store.insert(0, item);
rowEditing.startEdit(0, 0);
}
}, {
itemId: 'remove',
text: 'Удалить',
iconCls: 'remove-item',
handler: function() {
var sm = me.getSelectionModel();
rowEditing.cancelEdit();
Ext.Msg.show({
title: 'Удалить запись?',
msg: 'Вы действительно хотите удалить выделенные записи?',
buttons: Ext.Msg.YESNO,
icon: Ext.Msg.QUESTION,
fn: function (btn){
if (btn === 'yes') {
me.store.remove(sm.getSelection());
me.store.sync();
if (me.store.getCount() > 0) {
sm.select(0);
}
}
}
});
}
},{
text: 'Обновить',
iconCls: 'refresh',
handler: function() {
me.store.load();
}
}],
plugins: [
rowEditing
]
});
return me.grid;
}
});
Модуль товаров:
Ext.define('TourOffice.modules.products', {
getStore: function() {
var me = this;
if (me.store === undefined) {
Ext.define('product', {
extend: 'Ext.data.Model',
fields: ['id', 'category_id', 'name']
});
me.store = Ext.create('Ext.data.Store', {
model: 'product',
proxy: {
type: 'rest',
url : '/catalog/products/',
reader: {
type: 'json',
root: 'items'
}
},
autoLoad: true,
autoSync: true
});
var categories = Ext.create('Catalog.modules.categories');
me.categoriesStore = categories.getStore();
}
return me.store;
},
getIndexPanel: function() {
var me = this;
me.getStore();
var rowEditing = Ext.create('Ext.ux.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false
});
// create reusable renderer for categories
Ext.util.Format.comboRenderer = function(combo){
return function(value){
var record = combo.findRecord(combo.valueField, value);
return record ? record.get(combo.displayField) : combo.valueNotFoundText;
}
}
var categoriesCombo = new Ext.form.ComboBox({
typeAhead: true,
triggerAction: 'all',
lazyRender:true,
mode: 'local',
store: me.categoriesStore,
valueField: 'id',
displayField: 'name'
});
me.grid = Ext.create('Ext.grid.Panel', this, {
title: 'Товары',
store: me.store,
height: 700,
forceFit: true,
columns: [
{
text: 'Название товара',
dataIndex: 'name',
sortable : true,
editor: {
allowBlank: false
}
},{
text: 'Категория',
dataIndex: 'category_id',
editor: {
editor: currenciesCombo,
renderer: Ext.util.Format.comboRenderer(categoriesCombo), // pass combo instance to reusable renderer
allowBlank: false
}
}
],
viewConfig: {
stripeRows: true
},
tbar: [
{
text: 'Добавить',
iconCls: 'add-item',
handler: function() {
if (rowEditing.editing) return false;
var item = Ext.create('product', {
});
me.store.insert(0, item);
rowEditing.startEdit(0, 0);
}
}, {
itemId: 'remove',
text: 'Удалить',
iconCls: 'remove-item',
handler: function() {
var sm = me.getSelectionModel();
rowEditing.cancelEdit();
Ext.Msg.show({
title: 'Удалить запись?',
msg: 'Вы действительно хотите удалить выделенные записи?',
buttons: Ext.Msg.YESNO,
icon: Ext.Msg.QUESTION,
fn: function (btn){
if (btn === 'yes') {
me.store.remove(sm.getSelection());
me.store.sync();
if (me.store.getCount() > 0) {
sm.select(0);
}
}
}
});
}
},{
text: 'Обновить',
iconCls: 'refresh',
handler: function() {
me.store.load();
}
}],
plugins: [
rowEditing
]
});
return me.grid;
}
});
Вопрос - правильно ли я планирую структуру модулей?
В товарах есть создание класса категории categoriesStore для того, чтобы в комбобокс в листинге товаров подставилась категория, а не ID.
Прошу подсказать, есть ли недочеты или ошибки, как правильно построить такую структуру?
|
|
17.05.2012, 13:46
|
С++/C# modest developer
|
|
Регистрация: 07.11.2011
Сообщений: 244
|
|
приложение, в котором логика, представление и модель соседствуют (все в одном файле), как правило плохо масштабируется в дальнейшем. Дочерние компоненты обычно создаются во время инициализации(которую вызывает конструктор), у вас видимо в любое время. Но это только то что сильно бросается в глаза. А вообще похоже что вы уже приступили к реализации, а задаете вопрос
Сообщение от Alex Danilov
|
правильно ли я планирую структуру модулей
|
реализацией (написанием кода) обычно занимаются после того как спроектирован интерфейс и структура модулей.
|
|
17.05.2012, 15:11
|
Профессор
|
|
Регистрация: 04.02.2011
Сообщений: 1,815
|
|
Не буду столь критичен как Nekto_O,
Помоему для совсем маленького проекта не так уж и плохо.
но насчёт масштабируемости он прав, более дальновидным будет реализация модуля как папки с компонентами модуля..
Привиду пример, это частный случай, который позволяет грузить множество форм(модулей), с одинаковой структурой модуля.
Ext.define('app.modules.mngr', {
singleton: true,
getModule: function(me,name) {
//подгрузим типовые необходимые файлы для модуля
Ext.require([
"app.modules."+name+".view",
"app.modules."+name+".model",
"app.modules."+name+".store",
//"app.module."+name+".controller"-если нужен
],function(){
// создадим и добавим view
var myComponent = new app.modules[name].view();
me.add( myComponent );
//подключаем здесь ещё что нибудь
}
}
// вспомогательные функции,если надо (унечтожения модуля, хелперы и тд)
}
//применение
app.module.mngr.getModule(цель куда поместим загруженный модуль, имя модуля );
В вашем случае структура модулей может различатся.
как варианты
1)грузить только controller модуля, который будет грузить всё остальное. (самый гибкий)
2) файлы подлежащие загрузки описывать в module.cfg. (если у вас 100 видов документов по типу 1с,и их часто изменяют, то быстрее писать конфиги с нужной инфой, чем контролёры)
3) также некто не запрещает использовать requires внутри уже загруженных менеджером компонентов
__________________
Лучше калымить в гандурасе чем гандурасить на колыме
Последний раз редактировалось DjDiablo, 17.05.2012 в 16:17.
|
|
18.05.2012, 01:08
|
Новичок на форуме
|
|
Регистрация: 17.05.2012
Сообщений: 7
|
|
nekto_O, DjDiablo,
Спасибо за ответы, но я не понимаю, зачем модуль, содержащий 3-5 методов разбивать на файлы моделей, контроллеров? Методы будут следующие: отображение списка сущностей, редактирование выбранной сущности, получение Store для заполнения данных combobox в связанных таблицах.
|
|
18.05.2012, 12:49
|
Профессор
|
|
Регистрация: 04.02.2011
Сообщений: 1,815
|
|
Там не 3-5 методов !!! А 3-5 классов, обьявленных внутри методов (это большая разница !!!).
В ООП принято за правило, каждый класс выносить в отдельный файл.
Как минимум это способствует прозрачности и читаемости кода.
из самых очевидных недостатков вашего подхода "Всё в одном".
- С большим обьёмом кода сложно работать. Если такой модуль разрастется, его всё равно придётся бить на более мелкие.
- С этим модулем почти невозможно работать в команде.
- Если нужно сделать какой нибудь отчёт, и потребуется только store , то ради этого store придётся грузить весь модуль.
Но это только рекомендации, ваш проект работать и так будет .
Но всё таки гибкость и ориентированность на групповую работу, лучше закладывать сразу. Терять время на переделках это не путь джедая .
p.s.
Цитата:
|
А 3-5 классов, обьявленных внутри методов
|
Если поискать похожий паттерн, то это можно назвать фабричным методом. Однако фабрики не подразумевают объявление класса внутри функции, это скорее даже не обычно, а в большинстве языков невозможно. Обычно ограничиваются созданием экземпляров класса.
__________________
Лучше калымить в гандурасе чем гандурасить на колыме
Последний раз редактировалось DjDiablo, 18.05.2012 в 13:42.
|
|
18.05.2012, 19:16
|
Новичок на форуме
|
|
Регистрация: 17.05.2012
Сообщений: 7
|
|
DjDiablo,
Вас понял, спасибо.
А как по такому принципу сделать?
2) файлы подлежащие загрузки описывать в module.cfg. (если у вас 100 видов документов по типу 1с,и их часто изменяют, то быстрее писать конфиги с нужной инфой, чем контролёры)
|
|
18.05.2012, 23:28
|
Профессор
|
|
Регистрация: 04.02.2011
Сообщений: 1,815
|
|
для вашего проекта этого не нужно.
Когда я писал про конфиг я вообще то думал о проекте над которым сейчас работаю.
И имел я ввиду не просто конфиг а полноценный internal DSL,
В моём случае этот декларативный язык управляет структурой и поведением сущности на всех уровнях.
Такой подход это смесь декларативного и предметно ориентированного программирования.
К сожалению я не смогу нормально описать это решение в рамках форума.
Если интересно самому реализовать что то подобное, то пишите в личку. Сформилирую задание по клиенту, с подсказками в решении.
Но без лишний нужды я бы на вашем месте не заморачивался, в моём случае просто по другому невозможно. У вас же, всё намного проще.
__________________
Лучше калымить в гандурасе чем гандурасить на колыме
Последний раз редактировалось DjDiablo, 19.05.2012 в 01:10.
|
|
22.05.2012, 19:00
|
Новичок на форуме
|
|
Регистрация: 17.05.2012
Сообщений: 7
|
|
DjDiablo,
наверное пока не буду этим заниматься.
Подскажите, а где можно познакомиться с методами, как сделать всплывающее окно с формой при двойном клике на строку.
В grid это listener:
listeners: {
rowdblclick: {
fn: function(grid, rowIndex, e) {
var item = grid.getAt(rowIndex);
me.getEditForm(item);
}
}
}
А вот что должно быть в getEditForm смутно представляю. Наверное, там должно создаваться окно с формой редактирования. Но куда рендерить это? Может где-то есть пример?
Последний раз редактировалось Alex Danilov, 22.05.2012 в 19:02.
|
|
|
|