Faist

JavaScript Frameworks по-русски

Qooxdoo часть 10.1 Диалог редактирования данных о пользователях

Продолжаем работу над задачей, сформулированной в предыдущей статье.

Реализация функций редактирования данных

Переходим к добавлению и редактированию данных. В этих случаях должен выводиться один и тот же диалог с полями для редактирования и кнопками «Save» и «Cancel». По кнопке «Save» данные должны передаваться классу бизнес-логики, который далее должен вставить данные в таблицу postgres. Как говорится: слон большой, мы будем есть его по частям, поэтому сначала реализуем диалог редактирования данных, затем класс бизнес-логики, который будет что-то делать с данными (добавлять, обновлять), которые мы затем передадим в таблицу.

Диалог редактирования данных о пользователе

Создаем окно

Мы должны создать окно с формой для редактирования данных о пользователе и с нужными кнопками. Подобные диалоги мы уже создавали в учебном приложении для twitter. Я буду использовать некоторую информацию из предыдущих статей.

Итак, создадим новый класс окна UIWindow, добавим в него форму, разместим в ней нужные элементы управления

qx.Class.define("users.UIWindow", 
{ 
extend : qx.ui.window.Window,
construct : function()
{
this.base(arguments, this.tr("User info"));
}
});

Создаваемое окно будет содержать только одну форму, которая сама позаботится о своем форматировании. Поэтому для самого окна базового форматирования будет достаточно. Мы также сделаем окно модальным.

//Добавим разметку
var layout = new qx.ui.layout.Basic();
this.setLayout(layout);
this.setModal(true);

Добавляем форму

Теперь пришло время вставить в окно форму с нужными полями для ввода данных.

//Добавляем поля
var usrId = new qx.ui.form.TextField();
usrId.setReadOnly(true);
this.__form.add(usrId, this.tr("ID"), null, "Id");
var usrSurname = new qx.ui.form.TextField();
usrSurname.setRequired(true);
this.__form.add(usrSurname, this.tr("Surname"), null, "Surname");
var usrName = new qx.ui.form.TextField();
usrName.setRequired(true);
this.__form.add(usrName, this.tr("Name"), null, "Name");
var usrPatronymic = new qx.ui.form.TextField();
this.__form.add(usrPatronymic, this.tr("Patronymic"), null, "Patronymic");
var usrTelephone = new qx.ui.form.TextField();
this.__form.add(usrTelephone, this.tr("Telephone"), null, "Telephone");
var usrLogin = new qx.ui.form.TextField();
usrLogin.setRequired(true);
this.__form.add(usrLogin, this.tr("Login"), null, "Login");
var usrPassword = new qx.ui.form.PasswordField();
usrPassword.setRequired(true);
this.__form.add(usrPassword, this.tr("Password"), null, "Password");
var usrNote = new qx.ui.form.TextArea();
this.__form.add(usrNote, this.tr("Note"), null, "Note");

Переменную формы мы сделали приватным (два подчеркивания перед именем) членом класса потому, что будем обращаться к ней за пределами конструктора.

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

Обратите внимание на то, что некоторые поля помечены как обязательные для ввода. Например: usrSurname.setRequired(true). Рядом с такими полями будет отображаться звездочка. Обязательность заполнения поля позволит нам проводить простую проверку ввода данных.

Рассмотрим подробнее параметры метода add для формы. Первый параметр – переменная, указывающая на объект, который может быть на форме (текстовое поле, поле ввода пароля). Второй параметр – надпись у этого элемента, её мы обертываем функцией this.tr() для дальнейшего перевода. Третий параметр – функция проверки данных, в данном случае мы её не используем потому, что используем пометку обязательных для заполнения полей. Четвертый параметр – название для создания переменной, которая будет связана со значением поля. Диспетчер создаст для этих переменных функции доступа (get, set).

Свяжем данные с формой. Используем контроллер (диспетчер) данных.

var controller = new qx.data.controller.Form(null, this.__form);
this.__model = controller.createModel();

Точно также как в предыдущих случаях, мы создаем контроллер без модели. Затем мы просим контроллер создать модель для элементов формы. Эту модель мы будем использовать для сериализации данных формы.

Для формы также нужны кнопки «Save» для отправки данных на сервер и «Cancel» для закрытия окна. Добавляем кнопки.

//Добавим кнопки
var savebutton = new qx.ui.form.Button(this.tr("Save"));
this.__form.addButton(savebutton);
var cancelbutton = new qx.ui.form.Button(this.tr("Cancel"));
this.__form.addButton(cancelbutton);
cancelbutton.addListener("execute", function() {
this.close();
}, this);

Вот и все элементы, которые нам нужны, давайте отобразим их. Мы позволим встроенному в qooxdoo классу form.renderer позаботиться о размещении элементов на форме.

var renderer = new qx.ui.form.renderer.Single(form);
this.add(renderer);

Доступ к значениям формы

Подобно тому, как это сделано в MainWindow, мы будем использовать в новом окне события, чтобы сообщить другим частям нашего приложения об изменениях значений элементов формы. Как вы помните, блок кода «event» находится на том же уровне, что и блок кода конструктора.

events : {
"changeUserData" : "qx.event.type.Data"
},

Добавим обработчик кнопки «Save», который передаст данные из объекта model с проверкой данных в обработчик события изменения данных.

savebutton.addListener("execute", function() {
if(this.__form.validate()) {
var usrData = qx.util.Serializer.toJson(this.__model);
this.fireDataEvent("changeUserData", usrData);
this.close();
};
}, this);

Для проверки формы в класс Application добавим код создания формы.

this.__uiWindow = new twitter.UIWindow();
this.__uiWindow.moveTo(320,30);
this.__uiWindow.open();
И код обработчика события сохранения данных формы.
this.__uiWindow.addListener("changeUserData", function(e) {
this.debug("ChangeUserData: " + e.getData);
});

Сгенерируем приложение и откроем его в браузере. Форма ввода данных появляется при старте приложения.

Перевод интерфейса на русский язык

Для удобной дальнейшей работы переведем все на русский язык. Мы не забывали везде вставлять код this.tr() для названий, поэтому в переводе будем использовать возможности qooxdoo.

Сначала сообщим qooxdoo о том, что мы будем использовать еще и русский язык. Откройте в редакторе файл config.json и добавьте «ru» в строку "LOCALES" .

"LOCALES" : [ "en", "ru" ],

Затем запустим генератор с параметром translation.

./generate.py translation

После генерации в папке translation проекта отредактируйте файл ru.po.

Подробности мы уже рассматривали в одной из предыдущих статей.

Вот как примерно будет выглядеть ваш файл:

#
msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: you@your.org\n"
"POT-Creation-Date: 2011-01-12 19:36+0600\n"
"PO-Revision-Date: 2011-01-12 19:36+0600\n"
"Last-Translator: you <you@your.org>\n"
"Language-Team: Team <yourteam@your.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: users/Application.js:84
msgid "To remove records about user "
msgstr "Удалить данные о пользователе "
#: users/MUserInteraction.js:25 users/MUserInteraction.js:56
#: users/MUserInteraction.js:95 users/MUserInteraction.js:204
#: users/MUserInteraction.js:284
msgid "OK"
msgstr "Да"
#: users/UIWindow.js:59
msgid "Cancel"
msgstr "Выход"
#: users/MainWindow.js:6
msgid " The information on users"
msgstr "Информация о пользователях"
#: users/MainWindow.js:21
msgid "Add"
msgstr "Новый"
#: users/MainWindow.js:22
msgid "Add the information on the new user."
msgstr "Добавить информацию о новом пользователе"
#: users/MainWindow.js:28
msgid "Edit"
msgstr "Изменить"
#: users/MainWindow.js:29
msgid "Edit the information on the chosen user."
msgstr "Изменить информацию выбранного пользователя"
#: users/MainWindow.js:36
msgid "Delete"
msgstr "Удалить"
#: users/MainWindow.js:37
msgid "Delete the information on the chosen user."
msgstr "Удалить информацию о выбранном пользователе"
#: users/MainWindow.js:45 users/UIWindow.js:16
msgid "ID"
msgstr "№"
#: users/MainWindow.js:45 users/UIWindow.js:19
msgid "Surname"
msgstr "Фамилия"
#: users/MainWindow.js:45 users/UIWindow.js:22
msgid "Name"
msgstr "Имя"
#: users/MainWindow.js:46 users/UIWindow.js:24
msgid "Patronymic"
msgstr "Отчество"
#: users/MainWindow.js:46 users/UIWindow.js:26
msgid "Telephone"
msgstr "Телефон"
#: users/MainWindow.js:46 users/UIWindow.js:29
msgid "Login"
msgstr "Логин"
#: users/MainWindow.js:47 users/UIWindow.js:32
msgid "Password"
msgstr "Пароль"
#: users/MainWindow.js:47 users/UIWindow.js:34
msgid "Note"
msgstr "Примечание"
#: users/UIWindow.js:6
msgid "User info"
msgstr "Информация о пользователе"
#: users/UIWindow.js:39
msgid "Save"
msgstr "Сохранить"

После редактирования снова сгенерируйте приложение generate.py source и откройте его в браузере.

 

Table1

Как видим, интерфейс русский, обязательные поля помечены звездочкой. Убедитесь, что кнопки работают.

Связывание значений из таблицы с диалогом редактирования

Форма редактирования должна вызываться по кнопкам Новый и Изменить (в русском интерфейсе). При этом в случае изменения данных, в форму нужно передать значения из текущей строки таблицы.

Для реализации этого нам нужно создавать окно диалога в момент вызова обработчиков главного окна.

Мы поступим следующим образом: создадим объект диалога при старте приложения и будем отображать его при необходимости, заполняя или очищая в нем поля.

Уберите следующую строку кода из Application.js.

this.__uiWindow.open();

И перепишите обработчики событий NewUser и EditUser.

mainWnd.addListener("NewUser", function() {
this.debug("NewUser");
this.__uiWindow.setData(null);
this.__uiWindow.open();
}, this);
mainWnd.addListener("EditUser", function(e) {
this.debug("EditUser: " + e.getData());
this.__uiWindow.setData(e.getData());
this.__uiWindow.open();
}, this);

Заметили? Мы вызываем несуществующий метод диалога редактирования setData() для передачи в него некоторых данных.

Давайте реализуем этот метод в файле UIWindow.js.

Для этого добавим в него блок members (я вставил его перед блоком events).

members : {
setData : function (aUsrInfo){
if(aUsrInfo == null) {
this.__form.reset();
}
else if(aUsrInfo.length > 7)
{
this.__model.setId(aUsrInfo[0].toString());
this.__model.setSurname(aUsrInfo[1]);
this.__model.setName(aUsrInfo[2]);
this.__model.setPatronymic(aUsrInfo[3]);
this.__model.setTelephone(aUsrInfo[4]);
this.__model.setLogin(aUsrInfo[5]);
this.__model.setPassword(aUsrInfo[6]);
this.__model.setNote(aUsrInfo[7]);
}
}
},

В этом блоке я реализовал метод setData со следующей логикой: если переданный параметр == null, то я очищаю все поля формы, иначе я проверяю длину переданного массива данных и заполняю переменные модели нужными значениями.

Недостаток этого кода очевиден: мы изначально предполагаем, что данные в массиве расположены в нужном нам порядке, тем самым создавая тесную связь между разными частями кода. Это проходит в учебном примере, но для реальных задач нужно придумать что-нибудь более типизированное.

Но давайте убедимся, что все работает. Сгенерируйте и запустите приложение.

Выберите строку в таблице и нажмите кнопку изменить. Убедитесь, что все работает.

Table2

Попутно замечу, что мы не внесли ни одного изменения в файл MainWindow.js, поскольку использовали идею разделения кода по классам. Закройте диалог ввода информации и нажмите кнопку Новый.

Table1

Вы заметили, что у нас не так? Правильно: кнопка Сохранить активна, хотя поля пусты, а это противоречит нашему плану. И хотя сохранение не будет, пока не заполнены обязательные поля, все же активная кнопка может ввести пользователя в заблуждение. Исправим эту ситуацию.

Мы создадим у класса окна диалога метод onChange() и будем вызывать его при изменениях в полях формы. Метод onChange() будет вызывать функцию проверки формы и в зависимости от этого включать или отключать кнопку. Чтобы в методе увидеть кнопку, сделаем ее членом класса окна.

Итак, сначала заменим

var savebutton = new qx.ui.form.Button(this.tr("Save"));

на

this.__savebutton = new qx.ui.form.Button(this.tr("Save"));

И, соответственно, все вхождения savebutton на this.__savebutton.

Далее создадим метод окна onChange() в блоке members. Этот метод не должен быть виден за пределами класса, поэтому сделаем его приватным.

__onChange : function(){
this.__savebutton.setEnabled(this.__form.validate());
}

Если вы вставили этот метод в конец блока members, не забудьте поставить перед ним запятую, иначе – поставьте запятую после метода.

А теперь для каждого поля формы, кроме id, нужно написать обработчик похожий на следующий.

usrSurname.addListener("input", function(e) {
this.__onChange();
}, this);

Осталось сделать кнопку Save по умолчанию недоступной. Мы сделаем это в начале метода setData, поскольку при открытии окна диалога без внесения изменений сохранять нечего.

this.__savebutton.setEnabled(false);

Снова запустите приложение и убедитесь, что кнопка Save ведёт себя как надо.

Table3

Мы закончили работу с интерфейсом, в следующей статье перейдем к реализации логики в отдельном классе для взаимодействия с базой данных.

Qooxdoo часть 10. Работа с таблицами

В этой части мы научимся вставлять простейшую таблицу в окно qooxdoo.

Общая часть

Задание

Необходимо реализовать список пользователей с редактированием с соблюдением следующих условий:

  1. Для пользователя хранить следующие данные:
    • фамилия, имя, отчество;
    • телефон;
    • логин, пароль;
    • описание;
  2. Для хранения данных использовать postgres;
  3. В качестве сервера использовать mochiweb;
  4. Для вывода списка пользователей использовать таблицу qooxdoo;
  5. В окне вывода списка предусмотреть следующие кнопки: добавить, удалить, редактировать;
  6. При нажатии на кнопку «Редактировать», выводить окно с формой редактирования данных пользователя. В окне предусмотреть кнопки «Сохранить» и «Выход». После нажатия на кнопку «Сохранить» обновить данные о пользователе в базе данных.
  7. При нажатии на кнопку «Добавить» выводить окно с формой редактирования данных пользователя (аналогично п.6) с пустыми полями. Кнопка «Сохранить» должна быть недоступна, пока не введено фамилия пользователя.
  8. При нажатии на кнопку «Удалить» выводить окно с подтверждением удаления пользователя. После подтверждения – удалить запись о пользователе из базы.

Предварительные условия

Считаем, что у пользователя уже установлены и настроены следующие программы: Postgress, mochiweb, qooxdoo, epgsql (Erlang PostgreSQL Database Client).

В качестве операционной системы я буду использовать Ubuntu. Хотя это не принципиально.

Создаем базу данных и таблицу с данными о пользователе

План простой: создать базу данных для экспериментов под названием dbUsers, создать в ней таблицу users с нужными полями. Запускаем терминал.

1. Создаем базу.

sudo -u postgres createdb dbUsers

2. Создаем пользователя с правами суперюзера (естественно, имя пользователя и пароль в примере условные)

sudo -u postgres createuser faist -P
Enter password for new role: 
Enter it again: 
Shall the new role be a superuser? (y/n) y

3. Подсоединяемся к базе

psql -d dbUsers –U faist

4. После успешного соединения, создаем таблицу.

Запускаем редактор \е.

Набираем запрос:

create table users (id serial primary key, surname varchar(50), name varchar(50), 
     patronymic varchar(50), telephone varchar(12),login varchar(50), 
     password varchar(50),note varchar(250));

Выполняем запрос.

Проверяем наличие нашей таблицы: \d – выведет список таблиц, в том числе нашу.

Выполним запрос:

select * from users;

Ответ должен быть таким:

dbUsers=# select * from users;
id | surname | name | patronymic | telephone | login | password | note 
----+---------+------+------------+-----------+-------+----------+------
(0 rows)

Таблица и база готовы к наполнению.

Закрываем psql.

Создаем приложение на qooxdoo

Сначала определимся с именем приложения и местонахождением. Назовем приложение users а папка должна быть внутри корневой папки веб сервера mochiweb. Переходим в папку qooxdoo/tool/bin и запускаем генератор приложения.

./create-application.py --name=users --out=./../../../../job/js
>>> Copy skeleton into the output directory: ./../../../../job/js/users
>>> Patching file './../../../../job/js/users/Manifest.json'
>>> Patching file './../../../../job/js/users/generate.py'
>>> Patching file './../../../../job/js/users/config.json'
>>> Patching file './../../../../job/js/users/source/index.html'
>>> Patching file './../../../../job/js/users/source/class/users/Application.js'
>>> Patching file './../../../../job/js/users/source/class/users/theme/Decoration.js'
>>> Patching file './../../../../job/js/users/source/class/users/theme/Font.js'
>>> Patching file './../../../../job/js/users/source/class/users/theme/Color.js'
>>> Patching file './../../../../job/js/users/source/class/users/theme/Theme.js'
>>> Patching file './../../../../job/js/users/source/class/users/theme/Appearance.js'
>>> Patching file './../../../../job/js/users/source/class/users/test/DemoTest.js'
>>> Patching file './../../../../job/js/users/source/class/users/simulation/DemoSimulation.js'
>>> DONE

Приложение создано.

Перейдем в папку приложения и запустим

/job/js/users$ ./generate.py source

Далее открываем в браузере файл users/source/Index.html. Появится знакомая по предыдущим экспериментам кнопка.

FirstButton

Тестируем mochiweb

Давайте увидим эту кнопку через mochiweb. Запустим mochiweb и откроем файл по следующему пути http://localhost:8080/users/source/index.html. Скорее всего, у вас ничего не получится, потому что будет недоступна ссылка на библиотеки qooxdoo. Добавьте линки на qooxdoo. Я создал в корневой папке web подкаталог deps и добавил туда ссылку на qooxdoo. Соответственно в файле users/config.json исправил путь к qooxdoo следующим образом: "QOOXDOO_PATH" : "../../deps/qooxdoo". И, на всякий случай, поменяйте путь в файле generate.py. Затем снова генерируем ./generate.py source.

Теперь в браузере все должно быть хорошо.

План дальнейшей работы

Далее идем по пути, похожему на тот, что описан в предыдущих статьях.

  1. Создать класс главного окна приложения.
  2. В класс приложения добавить вызов главного окна, полюбоваться на него.
  3. Разместить в главном окне таблицу и отформатировать ее.
  4. Добавить в главное окно кнопки Добавить, Изменить, Удалить.
  5. Создать обработчики кнопок.
  6. Создать диалог редактирования записи о пользователе.
  7. Создать класс, отвечающий за бизнес-логику, который будет обрабатывать наши запросы, пока без обращения к базе данных.
  8. Когда все заработает, в классе бизнес-логики реализовать ajax запросы к mochiweb.
  9. Реализовать обработку запросов в mochiweb с обращением к базе данных.
  10. Протестировать наше приложение.

Вы можете спросить: зачем мы реализуем какие-то кнопки, не проще редактировать значение прямо в ячейке и сохранять новые значения по нажатию клавиши «Enter»? Отвечаю: многолетний опыт работы с пользователями говорит мне, что редактирование данных в ячейке в большинстве случаев - плохой путь. Пользователь может случайно изменить данные в ячейке, не желая этого. И в этом случае нам придется выводить всякие запросы о том, что данные изменились, хотите ли вы их сохранить и пр. И еще, пользователь ожидает более четких разделений функций просмотра и редактирования. Когда мы выводим форму для редактирования, пользователь понимает, что он реально может изменить данные и подходит к делу более ответственно. Впрочем, это только мои мысли.

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

Класс главного окна приложения

Создаем код класса главного окна с панелью инструментов и кнопками

На первом этапе создадим класс главного окна с разметкой, панелью инструментов и кнопками.

Как мы уже делали это, создадим файл MainWindow.js со следующим кодом.

qx.Class.define("users.MainWindow",
{
extend : qx.ui.window.Window,
construct : function()
{
this.base(arguments, " The information on users");
//скрываем кнопки окна
this.setShowClose(false);
this.setShowMinimize(false);
this.setShowMaximize(false);
//добавляем форматирование по сетке 
//и делаем растягивающейся первую ячейку второй строки
//в которой будет находиться таблица
var layout = new qx.ui.layout.Grid(0, 0);
layout.setRowFlex(1, 1);
layout.setColumnFlex(0, 1);
this.setLayout(layout);
//Добавляем панель инструментов
var toolbar = new qx.ui.toolbar.ToolBar();
this.add(toolbar, {row: 0, column: 0});
//Добавляем кнопки
//Кнопка добавления информации о новом пользователе 
var addButton = new qx.ui.toolbar.Button(this.tr("Add"));
addButton.setToolTipText(this.tr("Add the information on the new user."));
toolbar.add(addButton);
//Кнопка редактирования информации о пользователе
var editButton = new qx.ui.toolbar.Button(this.tr("Edit"));
editButton.setToolTipText(this.tr("Edit the information on the chosen user."));
toolbar.add(editButton);
//Кнопка удаления записи
var deleteButton = new qx.ui.toolbar.Button(this.tr("Delete"));
deleteButton.setToolTipText(this.tr("Delete the information on the chosen user."));
toolbar.add(deleteButton);
}
});

Напомню, что делает этот код. Мы создаем класс окна, наследуя его от qx.ui.window.Window. В конструкторе класса мы сначала скрываем системные кнопки окна, затем добавляем форматирование для элементов управления. Панель инструментов мы размещаем в первой ячейке первого столбца сетки. В панель управления мы добавляем кнопки, для которых указаны надписи и всплывающие подсказки. Все названия и подсказки мы заключаем в this.tr() для дальнейшего перевода.

Итак, начало положено. Теперь заменим в файле Application.js следующий код:

// Create a button
var button1 = new qx.ui.form.Button("First Button", "users/test.png");
// Document is the application root
var doc = this.getRoot();
// Add button to document at fixed coordinates
doc.add(button1, {left: 100, top: 50});
// Add an event listener
button1.addListener("execute", function(e) {
alert("Hello World!");
});

На:

var mainWnd = new twitter.MainWindow();
mainWnd.moveTo(30, 20);
mainWnd.open();

Генерируем код и проверяем (здесь и в дальнейшем я понимаю под этим выполнение команды ./generate.py source и просмотр результата в браузере). У вас получится что-то похожее на это:

ForTable1

Страшненько, но ничего, в дальнейшем мы все украсим.

Пора добавить таблицу.

Добавление таблицы

С таблицами мы еще не работали, поэтому в этой части есть кое-что новое.

Подробнее таблицы мы рассмотрим в одной из следующих статей, а пока нам надо знать следующее: для создания таблицы необходимо создать модель данных, которую мы свяжем с таблицей.

// table model
var tableModel = this.__tabMod = new qx.ui.table.model.Simple();
tableModel.setColumns([this.tr("ID"), this.tr("Surname"), this.tr("Name"),
this.tr("Patronymic"), this.tr("Telephone"), this.tr("Login"),
this.tr("Password"), this.tr("Note")]);
// table
var table = new qx.ui.table.Table(tableModel).set({
decorator: null
});
this.add(table, {row: 1, column: 0});

Генерируем и проверяем приложение.

UserTable1

Пока все идет как надо. Мы видим таблицу с нужными названиями столбцов. В таблице нет записей. Для экспериментов добавьте следующую строку перед созданием таблицы

tableModel.setData([[1,"Иванов","Иван","Иванович","123456","ivan","pwd","Нормальный пацан"],
[2,"Петров","Петр","Петрович","234567","petr","pwd1","Настоящий мужик"]
]);
// table
var table = new qx.ui.table.Table(tableModel).set({ ….

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

Добавляем обработку событий

В окне нужно обработать три события: добавление, редактирование и удаление информации о пользователе. Предлагаю подумать о том, в каком классе мы будем обрабатывать эти события? Если вы уже все знаете, можете пропустить пару абзацев.

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

Обработчики событий можно написать в классе MainWindow, но это противоречит идеи отделения интерфейса от бизнес-логики. Бизнес-логика будет описана в отдельном классе. Нам нужно будет создать объект этого класса, прежде чем обратиться к его методам (если они не статические, но это не наш случай). Где мы будем создавать объект класса? Если создать этот объект в классе MainWindow, установится зависимость между бизнес-логикой и интерфейсом. В этом случае класс MainWindow нельзя без переделок использовать в другом приложении. А какой класс характеризует именно наше приложение? Конечно класс Application. Именно этот класс отвечает за создание объектов классов нашего приложения и их взаимодействие.

Хорошо, обработчики событий создаем в классе Application. Возникает другая сложность: класс Application ничего не знает о наших кнопках и об их состояниях. У него есть объект класса MainWindow, в котором находятся кнопки. Что делать? Плохой путь: сделать доступными кнопки класса MainWindow снаружи. Хороший путь: объявить, что у класса MainWindow три события (по количеству кнопок), связать эти события с обработчиками кнопок, написать обработчики этих событий в классе Application. Именно этим мы сейчас и займемся.

Первый шаг: описываем события класса MainWindow.

events :
{
"NewUser" : "qx.event.type.Event",
"EditUser" : "qx.event.type.Data",
"DeleteUser" : "qx.event.type.Data"
}

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

Второй шаг: добавляем обработку событий кнопок и таблицы и связываем их с событиями окна.

Первая кнопка – добавление информации о новом пользователе. В данном случае данных еще нет, просто сообщаем о том, что мы хотим вывести форму для заполнения информации о новом пользователе. Поэтому используем firEvent.

addButton.addListener("execute", function() {
this.fireEvent("NewUser");
}, this);

Вторая кнопка – редактирование информации о пользователе. В этом случае мы должны взять информацию из текущей строки таблицы и передать ее для редактирования. Используем fireDataEvent.

editButton.addListener("execute", function() {
if(table.getFocusedRow() != null)
this.fireDataEvent("EditUser",this.__tabMod.getRowData(table.getFocusedRow()));
}, this);

Третья кнопка – удаление информации о пользователе. В этом случае достаточно передать только значение из первой, ключевой, колонки выделенной строки. Но мы хотим вывести пользователю осмысленный запрос об удалении информации и поэтому передаем данные всей строки.

deleteButton.addListener("execute", function() {
if(table.getFocusedRow() != null)
this.fireDataEvent("DeleteUser",this.__tabMod.getRowData(table.getFocusedRow()));
}, this);

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

А теперь добавим обработчики событий окна в класс Application. Пока реализуем вывод отладочной информации.

mainWnd.addListener("NewUser", function() {
this.debug("NewUser");
}, this);
mainWnd.addListener("EditUser", function(e) {
this.debug("EditUser: " + e.getData());
}, this);
mainWnd.addListener("DeleteUser", function(e) {
this.debug("DeleteUser: " + e.getData());
}, this);

Сгенерируем приложение и проверим вывод отладочной информации при нажатии на кнопки.

Приложение будет выглядеть вот так:

UserTable2

Явно много недоделок в интерфейсе, но этим мы займемся потом.

Включите отладочную консоль и понажимайте на кнопки. Убедитесь, что кнопки работают, и происходит передача информации из выбранной строки таблицы.

В следующей статье мы продолжим работу по нашему плану.