Как правильно спроектировать приложение?
Добрый день, коллеги!
Я проектирую простое приложение на основе 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. Прошу подсказать, есть ли недочеты или ошибки, как правильно построить такую структуру? |
приложение, в котором логика, представление и модель соседствуют (все в одном файле), как правило плохо масштабируется в дальнейшем. Дочерние компоненты обычно создаются во время инициализации(которую вызывает конструктор), у вас видимо в любое время. Но это только то что сильно бросается в глаза. А вообще похоже что вы уже приступили к реализации, а задаете вопрос
Цитата:
|
Не буду столь критичен как 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 внутри уже загруженных менеджером компонентов |
nekto_O, DjDiablo,
Спасибо за ответы, но я не понимаю, зачем модуль, содержащий 3-5 методов разбивать на файлы моделей, контроллеров? Методы будут следующие: отображение списка сущностей, редактирование выбранной сущности, получение Store для заполнения данных combobox в связанных таблицах. |
Там не 3-5 методов !!! А 3-5 классов, обьявленных внутри методов (это большая разница !!!).
В ООП принято за правило, каждый класс выносить в отдельный файл. Как минимум это способствует прозрачности и читаемости кода. из самых очевидных недостатков вашего подхода "Всё в одном". - С большим обьёмом кода сложно работать. Если такой модуль разрастется, его всё равно придётся бить на более мелкие. - С этим модулем почти невозможно работать в команде. - Если нужно сделать какой нибудь отчёт, и потребуется только store , то ради этого store придётся грузить весь модуль. Но это только рекомендации, ваш проект работать и так будет :) . Но всё таки гибкость и ориентированность на групповую работу, лучше закладывать сразу. Терять время на переделках это не путь джедая :). p.s. Цитата:
|
DjDiablo,
Вас понял, спасибо. А как по такому принципу сделать? 2) файлы подлежащие загрузки описывать в module.cfg. (если у вас 100 видов документов по типу 1с,и их часто изменяют, то быстрее писать конфиги с нужной инфой, чем контролёры) |
для вашего проекта этого не нужно.
Когда я писал про конфиг я вообще то думал о проекте над которым сейчас работаю. И имел я ввиду не просто конфиг а полноценный internal DSL, В моём случае этот декларативный язык управляет структурой и поведением сущности на всех уровнях. Такой подход это смесь декларативного и предметно ориентированного программирования. К сожалению я не смогу нормально описать это решение в рамках форума. Если интересно самому реализовать что то подобное, то пишите в личку. Сформилирую задание по клиенту, с подсказками в решении. Но без лишний нужды я бы на вашем месте не заморачивался, в моём случае просто по другому невозможно. У вас же, всё намного проще. |
DjDiablo,
наверное пока не буду этим заниматься. Подскажите, а где можно познакомиться с методами, как сделать всплывающее окно с формой при двойном клике на строку. В grid это listener:
listeners: {
rowdblclick: {
fn: function(grid, rowIndex, e) {
var item = grid.getAt(rowIndex);
me.getEditForm(item);
}
}
}
А вот что должно быть в getEditForm смутно представляю. Наверное, там должно создаваться окно с формой редактирования. Но куда рендерить это? Может где-то есть пример? |
| Часовой пояс GMT +3, время: 15:38. |