Перейти к содержанию

Работа с библиотекой ExtGWT: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Строка 195: Строка 195:


Для обновления содержимого панели после изменения нам необходимо обычно вызывать ее метод layout(). Библиотека ExtGWT будет делать это самостоятельно автоматически при установке метода панели setLayoutOnChange(true). Обратите внимание, что в нашем последнем примере компонент Text - это похожий на родной GWT-виджет Label (может содержать простой текст и не поддерживает HTML разметку).
Для обновления содержимого панели после изменения нам необходимо обычно вызывать ее метод layout(). Библиотека ExtGWT будет делать это самостоятельно автоматически при установке метода панели setLayoutOnChange(true). Обратите внимание, что в нашем последнем примере компонент Text - это похожий на родной GWT-виджет Label (может содержать простой текст и не поддерживает HTML разметку).

==== Окна (Window и Dialog) ====

Компонент Window реализует контейнер, который может свободно перемещаться в пределах окна браузера и своим поведением напоминает окна, широко использующиеся в обычном десктопном интерфейсе. Окна могут быть модальными (блокирующими остальные интерфейс при своей активации), видимыми и скрытыми, обычными и развернутыми на всю доступную область. Так как класс Window является наследником от ContentPanel, он имеет тот же самый визуальный вид и функционал, присущий панели.

Однако, в отличии от ContentPanel, объект Window не нужно добавлять в контейнер (или RootPanel приложения). При вызове метода show() класс Window добавляет соответствующий код в DOM-модель и устанавливает его z-индекс таким, чтобы окно отображалось над всеми другими элементами. Далее производится вызов функции раскладки дочерних элементов и центровка нового окна относительно рабочей области.

Если вы после создания и завершения работы окна хотите в дальнейшем повторно его отобразить, то предпочтительно вместо полного уничтожения объекта-окна и освобождения зарезервированных под него ресурсов, выполнить его скрытие и повторное отображение. Рассмотрим далее пример использования Window:

<source lang=java>
// создание объекта окна
Window w = new Window();

// установка текста заголовка
w.setHeading("Информация о продукте");

// делаем окно модальным
w.setModal(true);

// установка размеров
w.setSize(600, 400);

// кнопка разворота на всю доступную область
w.setMaximizable(true)

// задание всплывающей подсказки
w.setToolTip("Домашняя страница ExtGWT...");

// содержимое окна будет является web-страница
w.setUrl("http://www.extjs.com/products/gxt/");

// отображение окна
w.show();
</source>

Если вы обратили внимание, мы вызвали метод setUrl в нашем коде, который добавляет в приложение iframe и отображает в нем содержимое по заданному URL. Хост-браузер Google Web Toolkit при отладке выдаст предупреждение о риске для безопасности, так как в этом случае содержимое потенциально опасно и не относится к нашему приложению.


== Глава 5. Работа с данными ==
== Глава 5. Работа с данными ==

Версия от 16:32, 17 мая 2009

Глава 1. Обзор библиотек ExtGWT и GWT

Библиотеки ExtGWT и GWT представляют собой мощнейшее решение для разработки web-приложений, которые выглядят и обладают всеми функциями традиционных десктопных приложений. Разработчики, знающие язык программирования Java, могут использовать свой ранее накопленный опыт и существующие наработки для создания современного программного обеспечения. Стоит отметить, что ваше приложение не будет привязано к определенному контейнеру сервлетов, и вы можете создавать и запускать его даже дома на локальном компьютере. Мы также рассмотрим использование популярной среды разработки Eclipse, однако вы не будете ограничены каким-то одним программным продуктом и сможете работать с тем набором средств разработки, к которому уже привыкли.

В этой главе вы кратко познакомитесь с основными возможностями ExtGWT. Мы также поверхностно рассмотрим особенности библиотеки GWT, основные приемы ее использования и сборки приложений.

Немного об ExtGWT

Библиотека ExtGWT (также имевшая ранее название GXT) - это основанное на фреймворке Google Web Toolkit решение, предназначенное для построения высококлассных пользовательских интерфейсов web-приложений, разрабатываемое компанией ExtJS, которой принадлежит одноименная JavaScript библиотека.

Так как ExtGWT является надстройкой над мощной системой от Google, вместо изобретения очередного велосипеда она просто расширяет базовые возможности этой платформы, привнося в нее дополнительные компоненты, различные варианты их размещения, возможность работы с моделями данных и кэширующей их подсистемой. Библиотека начала свою жизнь под именем "MyGWT", а в дальнейшем ее главный разработчик присоединился к компании ExtJS и значительно расширил ее возможности, практически переписав некоторые ее части с нуля. На сегодняший момент существует уже вторая версия - ExtGWT 2.0, которая полностью покрывает потребности в компонентах типового RIA приложения.

Краткий перечень возможностей ExtGWT:

  • Компоненты для отображения и редактирования данных в их различных представлениях: Grid, List, DataView, Tree, Table
  • Панели, табы и методы автоматического расположения визуальных компонентов
  • Расширенные возможности по работе с окнами, диалогами, сообщениями и информационными панелями
  • Поддержка работы с формами ввода данных как с простым, так и с форматированным текстом, полей для ввода чисел, паролей, выпадающих списков, календарей, а также других элементов
  • Кнопки, всплывающие подсказки, панели инструментов, панели статуса и меню
  • Локальные кэширующие хранилища объектов данных, их автоматические загрузчики и модели данных, позволяющие легко взаимодействовать с компонентами библиотеки
  • Возможность создания интерактивных портальных и имитирующих десктоп web-приложений, написанных с использованием фреймворка MVC
  • Большой выбор графических эффектов, таких как изменяемые размеры и drag-n-drop для компонентов и их контейнеров
Информация

Произношение слова "ExtGWT": Хотя это не официальное, но наиболее часто используемое среди англоязычных разработчиков: "и-экс-ти джи-дабл-ю-ти", что совпадает с произношением по отдельности слов ExtJS и GWT

Лицензирование: ExtGWT имеет двойную лицензию и доступна как вариант с открытым исходным кодом под GPLv3, так и под коммерческой, которую выбирают разработчики, кому по тем или иным причинам не подходит GPL

Глава 2. Подготовка среды разработки

Вообще организация среды для разработки для типового приложения с библиотекой ExtGWT практически не отличается от проектов, использующих только Google Web Toolkit: необходимо выполнить всего пару дополнительных движений. В этой главе Вы узнаете как установить и подготовить среду разработки Eclipse и создать основной каркас приложения.

Что потребуется

Глава 3. Виджеты и все, что с ними связано

Любая библиотека, которая помогает создавать «богатые web-приложения» (или по-английски Rich Internet Applications — RIA), прежде всего должна содержать широкий выбор интерфейсных элементов (или виджетов). Рассматриваемая нами библиотека ExtGWT содержит более 10 различных пакетов, содержащих более 210 классов, предназначенных для работы с виджетами. Каждый из таких элементов имеет множество настраиваемых параметров и позволяет изменять свой внешний вид и дизайн, используя механизм тем оформления. Ниже мы рассмотрим приложение Explorer, которое содержит все доступные элементы библиотеки и служит хорошим подспорьем для изучения ее работы.

Пакет .widget

Базовый пакет com.extjs.gxt.ui.client.widget содержит наиболее часто используемые классы компонентов, контейнеров и виджетов общего назначения. Обычно именно с одного из виджетов, находящихся в этом пакете, вы начнете создавать новое приложение.

Component

Все виджеты библиотеки ExtGWT используют класс Component как родительский класс, либо наследуя его напрямую, либо наследуя его дочерний класс BoxComponent, в том случае, если элементу управления требуются функции изменения размера и позиционирования. Все дочерние классы, образованные от Component, автоматически управляются ядром библиотеки на протяжении всего цикла работы web-приложения (в том числе поддержка операций подсоединения и отсоединения виджета от DOM-модели браузера). Им также обеспечивается автоматическая поддержка скрытия и повторного отображения (hide/show) и отключения и включения (disable/enable) элементов управления. Класс Component позволяет своим дочерним классам использовать ленивое отображение в любом контейнере библиотеки ExtGWT. Это дает возможность таким контейнерам производить отображение видимых дочерних элементов (а следовательно выполнять дорогостоящие операции по работе с DOM-моделью в браузере пользователя и выделению памяти) только один раз при позиционировании самого контейнера.

Сам же класс Component является наследником класса Widget библиотеки GWT, что позволяет использовать все виджеты библиотеки ExtGWT в обычных GWT-приложениях. Все компоненты, подключаемые к панели GWT (класс Panel) будут отображены сразу же после операции добавления.

Container

Контейнеры — это виджеты, которые содержат другие компоненты. Они берут на себя всю заботу о жизненном цикле своих дочерних элементов, их создании, подключении и отключении от DOM-модели. Контейнеры также выполняют необходимые действия по правильному позиционированию и изменению размеров своих компонентов. К стандартным виджетам, которые являются контейнерами, можно отнести компоненты ButtonBar, HtmlContainer, Menu, Portal, Table, TabPanel, ToolBar и Tree.

LayoutContainer

Если вы уже имели опыт создания обычных приложений с графическим интерфейсом в средах Visual Studio, Delphi и тому подобных, то наверное помните их визуальные редакторы, позволяющие расположить несколько кнопочек, элементов ввода, редакторов текста в точно в заданных координатах оконного интерфейса. Разработчику было явно видно, что первая кнопка расположена в 8 пикселях от второй, а ширина всего диалогового окна жестко задана в 200 пикселей и не менялась пользователем, так как иначе могла «поехать» вся остальная разметка элементов.

Почему теперь такой подход считается неправильным? Да потому, что во-первых с тех пор кардинально изменилось число устройств, на которых могут работать приложения, а их технические характеристики выросли не только вверх, но и вниз. Одно и тоже приложение должно хорошо себя представлять как на 20" мониторе, установленном на рабочем столе, так и на маленьком экране нетбука с диагональю 7". Кроме того, обратите внимание на различную величину параметра DPI (число точек на дюйм) для различных устройств: число пикселей, которые помещаются в реальный физический размер экрана может отличаться в несколько раз, таким образом делая реальный отображаемый размер элементов или слишком маленьким, или наоборот слишком большим.


Мне сначала было сложно подобрать адекватный перевод технического термина layout, который используется как в документации Google Web Toolkit, так и библиотеки виджетов ExtGWT. Совершенно очевидно, что слово произошло фразы lay out, которая дословно переводится «выкладывать». Давайте в дальнейшем определимся, что наиболее подходящим русскоязычным аналогом мы будем считать термин раскладка.

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

Далее применительно к библиотеке ExtGWT мы рассмотрим компонент LayoutContainer, который является базовым контейнером, имеющим функции автоматической раскладки своих дочерних элементов. Его основной особенностью является возможность подключения различных вариантов раскладки элементов, позволяющую реализовать практически любой дизайн интерфейса приложения. По умолчанию в LayoutContainer используется тип раскладки FlowLayout, который подразумевает стандартное для HTML разметки расположение элементов (первый виджет прикрепляется к верхнему левому углу области, далее они выстраиваются сверху вниз). В связи с тем, что в ExtGWT реализовано много вариантов раскладки элементов, мы вернемся к их подробному рассмотрению в Главе 4, а на данном этапе просто сфокусируемся на основных функциях компонента LayoutContainer.

Далее предлагаю открыть редактор кода Eclipse и реализовать небольшой пример создания контейнера, добавления к нему нового виджета и подключения к основному объекту приложения RootPanel:

    // создание нового объекта LayoutContainer
    LayoutContainer container = new LayoutContainer();

    // создание и добавление в контейнер кнопки
    container.add(new Button("Нажми меня"));

    // жесткая установка ширины и высоты контейнера в 300 пикселей
    container.setSize(300,300);

    // задание отображения рамки
    container.setBorders(true);

    // подключение элемента-контейнера к DOM-модели приложения
    RootPanel.get().add(container);

    // "выкладка" контейнера - отображение всех дочерних элементов и выстраивание по заданному алгоритму
    container.layout();


По умолчанию компонент LayoutContainer не имеет никакого визуального изображения, поэтому бывает достаточно сложно определить его границы на экране. Для отладки вы можете попробовать использовать метод setBorders(true), который отобразит внешние границы контейнера. Помимо всего прочего, это даст возможность найти проблемы при отображении дочерних компонентов при той или иной раскладке.

Обратите внимание, что в нашем случае (когда компонент LayoutContainer помещается напрямую в DOM-модель, а не в другой существующий контейнер с заданным типом раскладки), необходимо явно задать ему размеры и вызвать метод layout(), который произведет операцию размещения и отображения его дочерних элементов. Это мы и сделали в последней строке примера.

HorizontalPanel и VerticalPanel

Компоненты HorizontalPanel и VerticalPanel реализуют свою функциональность путем использования стандартного в HTML разметке тега table. Эти контейнеры предназначены для упрощения раскладки элементов в совсем тривиальных случаях:

  • HorizontalPanel: размещает дочерние элементы в один единственный ряд слева направо, используя раскладку TableRowLayout
  • VerticalPanel: размещает дочерние элементы в одну единственную колонку сверху вниз, используя раскладку TableLayout

В следующем примере мы добавим два компонента Label (представляющего собой текстовый элемент) к горизонтальной панели, указав применительно ко второму дополнительные параметры, также называемые данные раскладки (layout data). Так как мы используем раскладку типа TableLayout, то дополнительные параметры должны содержаться в соответствующем ему объекте TableData. В нашем случае мы укажем необходимость выровнять текст во второй ячейке по правому краю.

    // создание объекта - горизонтальной панели
    HorizontalPanel hp = new HorizontalPanel();

    // установка ширины в пикселях
    hp.setWidth(300);

    // установка ширины встроенной таблицы
    hp.setTableWidth("100%");

    // добавление к панели компонента Label
    hp.add(new Label("Выровнено по центру"));

    // создание объекта TableData (содержащего дополнительные параметры раскладки)
    TableData td = new TableData();

    // задание выравнивания текста по правому краю
    td.setHorizontalAlign(HorizontalAlignment.RIGHT);

    // добавление к панели компонента Label с дополнительными параметрами по раскладке
    hp.add(new Label("Выровнено по правому краю"), td);

    // подключение элемента-контейнера к DOM-модели приложения
    RootPanel.get().add(hp);

Дополнительные данные о раскладке используются библиотекой в тот самый момент, когда происходит процесс их выстраивания. С их помощью можно задавать информацию о размере, границах вокруг элементов и их позиционировании. Далее мы более подробнее рассмотрим их использование в Главе 4.

ContentPanel

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

Класс ContentPanel является классом-наследником контейнера LayoutContainer, но вдобавок ко всем его методам и свойствам имеет дополнительные элементы оформления: отдельный заголовок, функции сворачивания/разворачивания содержимого, возможность отображения рамки и подключения специальных панелей соответственно вверху и внизу элемента. Также как и для LayoutContainer, компоненту ContentPanel должны быть явно заданы размеры, в том случае, если панель не помещается в другой контейнер с определенной раскладкой.

Давайте засучим рукава и напишем код, который создаст новую панель и выведет в область окна браузера:

    // Создание объекта - панели
    ContentPanel cp = new ContentPanel();

    // Установка заголовка
    cp.setHeading("Заголовок");
    
    // Явное задание размера панели
    cp.setSize(250, 140);
    
    // Смещение панели на 10 пикселей к низу и вправо относительно
    // родительского контейнера
    cp.setPosition(10, 10);

    // Активация функции сворачивания
    cp.setCollapsible(true);

    // Придание панели закругленных углов
    cp.setFrame(true);

    // Установка белого фона для тела панели
    cp.setBodyStyle("backgroundColor: white;");

    // Добавление к заголовку панели дополнительных кнопок
    cp.getHeader().addTool(new ToolButton("x-tool-gear"));
    cp.getHeader().addTool(new ToolButton("x-tool-close"));

    // Добавление произвольной HTML разметки в тело панели
    cp.addText("Текст <b>жирный</b>");

    // Кнопка OK в нижней области панели
    cp.addButton(new Button("OK"));

    // Установка иконки с папкой в верхнем левом углу панели
    cp.setIconStyle("tree-folder-open");

    // Подключение панели к корневому элементу
    RootPanel.get().add(cp);

В панель можно динамически добавлять компоненты после ее первоначальной отрисовки. Измененный пример:

    cp.addText("Текст <b>жирный</b>");

    // Кнопка OK в нижней области панели
    Button btn = new Button("OK");
    cp.addButton(btn);
    btn.addSelectionListener(new SelectionListener<ButtonEvent>() {

        @Override
        public void componentSelected(ButtonEvent be) {
            cp.add(new Text("Текст"));
            cp.layout();
        }
           
    });

В этом случае мы создали объект Button (кнопка), а затем воспользовались анонимным классом, реализующим интерфейс SelectionListener, в котором переопределили метод componentSelected, который вызывается при нажатии этой кнопки. Таким образом мы создали обработчик события типа ButtonEvent, выполняющий добавление нового компонента с надписью "Текст" и производящий перекомпоновку содержимого панели.

В классическом случае мы бы могли создать новый класс (называющийся например, BtnSelectionListener), а уже он него создать объект-обработчик события и привязать его к кнопке. Однако, так как в нашем случае логика обработки события тривиальна и нет необходимости привязывать обработчик в нескольким визуальным компонентам, использование анонимного класса сделало наш код компактным и более читаемым. Следует отметить, что среда разработки Eclipse всячески способствует быстрому написанию кода, в большинстве случаев "угадывая" нужные нам типы событий и подставляя целые шаблоны кода, что дает разработчику возможность сконцентрироваться на бизнес-логике приложения, не отвлекаясь на написание и отладку интерфейсного кода.

Для обновления содержимого панели после изменения нам необходимо обычно вызывать ее метод layout(). Библиотека ExtGWT будет делать это самостоятельно автоматически при установке метода панели setLayoutOnChange(true). Обратите внимание, что в нашем последнем примере компонент Text - это похожий на родной GWT-виджет Label (может содержать простой текст и не поддерживает HTML разметку).

Окна (Window и Dialog)

Компонент Window реализует контейнер, который может свободно перемещаться в пределах окна браузера и своим поведением напоминает окна, широко использующиеся в обычном десктопном интерфейсе. Окна могут быть модальными (блокирующими остальные интерфейс при своей активации), видимыми и скрытыми, обычными и развернутыми на всю доступную область. Так как класс Window является наследником от ContentPanel, он имеет тот же самый визуальный вид и функционал, присущий панели.

Однако, в отличии от ContentPanel, объект Window не нужно добавлять в контейнер (или RootPanel приложения). При вызове метода show() класс Window добавляет соответствующий код в DOM-модель и устанавливает его z-индекс таким, чтобы окно отображалось над всеми другими элементами. Далее производится вызов функции раскладки дочерних элементов и центровка нового окна относительно рабочей области.

Если вы после создания и завершения работы окна хотите в дальнейшем повторно его отобразить, то предпочтительно вместо полного уничтожения объекта-окна и освобождения зарезервированных под него ресурсов, выполнить его скрытие и повторное отображение. Рассмотрим далее пример использования Window:

// создание объекта окна
Window w = new Window();

// установка текста заголовка
w.setHeading("Информация о продукте");

// делаем окно модальным
w.setModal(true);

// установка размеров
w.setSize(600, 400);

// кнопка разворота на всю доступную область
w.setMaximizable(true)

// задание всплывающей подсказки
w.setToolTip("Домашняя страница ExtGWT...");

// содержимое окна будет является web-страница
w.setUrl("http://www.extjs.com/products/gxt/");

// отображение окна
w.show();

Если вы обратили внимание, мы вызвали метод setUrl в нашем коде, который добавляет в приложение iframe и отображает в нем содержимое по заданному URL. Хост-браузер Google Web Toolkit при отладке выдаст предупреждение о риске для безопасности, так как в этом случае содержимое потенциально опасно и не относится к нашему приложению.

Глава 5. Работа с данными

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

В этой главе описано, как библиотека ExtGWT оперирует данными, позволяя различным компонентам приложения получать к ним доступ. Используя объекты типа ModelData и BeanModel, а также локальную систему их хранения и загрузки, вы сможете получить максимум от потенциала таких мощных компонентов как Grid, Table, Tree и ListView.

Модели данных, хранилища и их загрузчики

В этом разделе мы подробно рассмотрим что представляют собой эти термины и их взаимодействие между собой.

Модели

Давайте представим, что мы пишем сложное web-приложение с использованием возможностей Google Web Toolkit, состоящее из двух независимых частей: серверной, которая будет иметь доступ к некоей базе данных, и клиентской, выполняющейся в конечном виде на языке JavaScript в браузере. Очевидно, что с учетом всех достоинств GWT клиентскую часть можно сделать такой же "умной" как и серверную: добавить возможность хранить промежуточные результаты работы между обращениями приложения к серверу, манипулировать этими данными и даже умудряться строить отчеты. Вполне логично, что разработчик захочет иметь для клиентского и серверного кода единый механизм работы с такими данными, и например свести объекты данных в классы типа POJO (Plain Old Java Object - старый добрый Java объект). Затем объекты этих классов, заполненные реальными данными, можно будет организовывать в коллекции, списки, хэши и так далее.

А теперь давайте посмотрим на это с точки зрения разработчика универсальной библиотеки, такой как ExtGWT. Мы пишем компоненты, которые независимо от типа объекта должны иметь возможность знать о количестве, типе, составе его полей и уметь динамически определять другие параметры. На стороне сервера мы воспользуемся отражением (технология Java Reflection), таким образом определив все поля нужного нам класса, однако на стороне браузера подобного механизма нет - ведь наш код кросскомпилирован в JavaScript!

Тут на выручку приходит интерфейс, называющийся моделью данных. Во многих фреймворках по существу, это класс, в котором описаны поля того или иного типа объекта, их границы и другие параметры бизнес логики. Для ExtGWT - это также интерфейс, который берет на себя работу по внутреннему отображению данных с учетом особенностей кросскомпилированного JavaScript-кода.

Рассмотрим пример. Допустим, в приложении нам требуется оперировать данными по акциям, для чего мы воспользуемся определением модели, заданной новым классом Stock. Определяем модель:

  • Создаем новый класс Stock в пакете пакет_нашего_приложения.client.data, указав в качестве родительского класс com.extjs.gxt.ui.client.data.BaseModel
  • Определяем конструктор по умолчанию без аргументов:
package com.myapp.client.data;

import com.extjs.gxt.ui.client.data.BaseModel;

public class Stock extends BaseModel {

	public Stock() {
		
	}
}

Обратите внимание, что важный шаг - для класса, у которого отсутствует конструктор без аргументов, сериализация (а значит передача данных между клиентом и сервером) будет невозможна.

  • Для удобства определяем необходимое число реальных конструкторов модели с аргументами, являющимися начальными данными для объектов. Например, в нашем случае можно будет задать описание новой акции двумя способами:
// компания, тикер, цена открытия и цена закрытия
public Stock(String name, String symbol, double open, double last) {

}
   
// компания, цена открытия, изменение цены, процент изменения, дата и описание индустрии
public Stock(String name, double open, double change, double pctChange, Date date, String industry) {
        
}
  • Далее в конструкторах напишем код, который будет помещать данные, указанные при создании нового объекта в его структуру. Любой класс-модель имеет два базовых метода установления новых типизированных значений полям модели и их извлечение: public <X> X set(java.lang.String property, X value) и public <X> X get(java.lang.String property).

Начнем с примера как не надо делать и рассмотрим измененный код конструкторов:

public Stock(String name, String symbol, double open, double last) {
    set("name", name);
    set("symbol", symbol);
    set("open", open);
    set("last", last);
    // Текущая дата
    set("date", new Date());
    // Здесь это вычисляемое значение - изменение равно цена закрытия минус цена открытия
    set("change", last - open);
}
    
public Stock(String name, double open, double change, double pctChange, Date date, String industry) {
    set("name", name);
    set("open", open);
    set("change", change);
    set("percentChange", pctChange);
    set("date", date);
    set("industry", industry);
}

Теперь где-нибудь у себя в коде мы можем обратиться к объекту класса Stock и получить значение его поля:

    double stock1Open = (Double)stock1.get("open")

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

public Stock(String name, String symbol, double open, double last) {
    setName(name);
    setSymbol(symbol);
    setOpen(open);
    setLast(last);
    // Текущая дата
    setDate(new Date());
    // Здесь это вычисляемое значение - изменение равно цена закрытия минус цена открытия
    setChange(last - open);
}
    
public Stock(String name, double open, double change, double pctChange, Date date, String industry) {
    setName(name);
    setOpen(open);
    setChange(change);
    setPctChange(pctChange);
    setDate(date);
    setIndustry(industry);
}

public String getName() {
    return (String)get("name");
}

public void setName(String name) {
    set("name", name);
}

public String getSymbol() {
    return (String)get("symbol");
}

public void setSymbol(String symbol) {
    set("symbol", symbol);
}

public double getOpen() {
    return (double)get("open");
}

public void setOpen(double open) {
    set("open", open);
}

public double getLast() {
    return (double)get("last");
}

public void setLast(double last) {
    set("last", last);
}

public double getChange() {
    return (double)get("change");
}

public void setChange(double change) {
    set("change", change);
}

public Date getDate() {
    return (Date)get("date");
}

public void setDate(Date date) {
    set("date", date);
}

public double getPctChange() {
    return (double)get("pctChange");
}

public void setPctChange(double pctChange) {
    set("pctChange", pctChange);
}

public String getIndustry() {
    return (String)get("industry");
}

public void setIndustry(String industry) {
    set("industry", industry);
}

Данные теперь можно получить как в примере выше:

    double stock1Open = (Double)stock1.getOpen()

Что нам это дало? Теперь мы можем контролировать присваиваемые значения отдельным полям:

public void setName(String name) {
    // тикер акции не может содержать более 4 символов
    if (name.length() > 4) {
        // в противном случае - обрезать до 4 символов
	set("name", name.substring(1, 4));
    } else {
        set("name", name);
    }
}

Мы рассмотрели лишь один из возможных в библиотеке ExtGWT способов создания объектов данных и далее еще вернемся к этому вопросу.

Информация

В простейшем случае можно обойтись без собственных классов моделей, создавать объекты-потомки класса BaseModel и напрямую задавать их свойствам значения:

BaseModel model1 = new BaseModel();
model1.set("text", "N/A");
// помещаем объект в хранилище
store.add(model1);

Хранилища (Store)

Библиотека ExtGWT имеет в своем составе достаточно удобные возможности по хранению однотипных объектов (которые в большинстве своем представлены моделями) на стороне браузера. Некоторые визуальные компоненты (такие как ListView, Grid, ComboBox) умеют напрямую обращаться к хранилищу и работать с находящимися там данными, другие же (как Table, Tree, DataList) требуют дополнительную функцию-прослойку. Класс Store и его наследники реализуют различные варианты локальных хранилищ с поддержкой кэширования данных какие только могут потребоваться приложению. Вы как разработчик можете воспользоваться их встроенными функциями сортировки, фильтрации и изменения локального набора данных. Кроме того, хранилище использует систему управления событиями, которая позволяет реагировать на появление новых данных или, например, изменения направления их сортировки.


Хранилище содержит в себе фактически две коллекции объектов: модели (объекты Model) определенного типа, которые были получены в результате загрузки, например, из БД и записи (объекты Record), которые сопоставляются конкретным моделям и обозначают изменения, внесенные, но не закоммиченные во внешнее хранилище. Учтите, что коллекция записей очищается только в случае принятия данных (commit) или отката (rollback), но остается неизменной при загрузке новых данных. Разделение данных на модели (Model) и их записи (Record) имеет преимущество в плане возможности отслеживания вносимых изменений для каждой порции данных и позволяет составлять сколь угодно сложные алгоритмы синхронизации между локальным и удаленным хранилищем.

Основные типы хранилищей: ListStore (для хранения списка данных), GrouppingStore (список данных с возможностью группировки), TreeStore (древовидное хранилище данных).

Пример работы с хранилищем:

ListStore store = new ListStore();
BaseModel model1 = new BaseModel();
model1.set("id", 100);

// помещаем объект в хранилище
store.add(model1);

// выполняем поиск по любому полю модели
// будет возвращен первый объект, который удовлетворяет критериям поиска
BaseModel found = store.findModel("id", 100);

// полностью очищаем содержимое хранилища
store.removeAll()

Практическое применение - ComboBox

Компонент ComboBox - это элемент "выпадающий список", который позволяет пользователю приложения произвести выбор какого либо значения на основе заранее сформированного списка данных. Обычно он используется в формах, однако так как является самодостаточным элементом, никто не запрещает поместить его отдельно от других элементов ввода (например, в углу окна для быстрого выбора языка интерфейса).

Главной отличительной особенностью ComboBox от других элементов ввода данных является то, что его нельзя использовать без создания специального хранилища значений, из которого он их и погружает: напрямую добавлять значения в список невозможно. Тип хранилища - ListStore.

Простой пример:

// создание нового объекта ComboBox
ComboBox combo = new ComboBox();

// создание нового объекта для хранения списка моделей
ListStore store = new ListStore();
        
// первый объект данных: свойство text со значением "N/A"
BaseModel model1 = new BaseModel();
model1.set("text", "N/A");
store.add(model1);

// второй объект данных: свойство text со значением "My Item"
BaseModel model2 = new BaseModel();
model2.set("text", "My Item");
store.add(model2);

// подключение хранилища к ComboBox
combo.setStore(store);
        
// добавление визуального элемента к DOM-модели приложения
RootPanel.get().add(combo);

По умолчанию в ComboBox отображается содержимое свойства text модели, это поведение можно изменить с помощью метода setDisplayField("name"). Результат выбора пользователем в текстовом виде можно получить через метод getRawData():

combo.addSelectionChangedListener(new SelectionChangedListener<BaseModel>() {

    @Override
    public void selectionChanged(SelectionChangedEvent<BaseModel> se) {
        Info.display("Выбрано", combo.getRawValue());
    }
            
});

Или как объект модели с получением ее необходимых свойств:

        Info.display("Выбрано", (String)comb.getValue().get("text"));

Для получения выделенного значения можно использовать методы getValue() или getSelectedRecord().

добавить примеров

Из программного кода можно устанавливать выбранное значение по умолчанию с помощью методов setValue(объект_модели) или setRawValue("текст"). В первом случае присваиваемое значение будет проходить валидацию, а во втором - устанавливаться как есть.

добавить примеров