Как правильно спроектировать приложение?
Добрый день, коллеги!
Я проектирую простое приложение на основе 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, время: 00:11. |