JavaScript-библиотеки Ext JS и Ext GWT известны, помимо прочего, одним из лучших наборов визуальных компонентов — как по дизайну, так и по кроссбраузерности, да и по стабильности работы. Потому и руки сами тянутся к любому проекту на GWT добавить Ext и заменить скучные гугловые виджеты на симпатичные формы и окошки.
Однако интеграция Ext GWT и GWT до сих пор оставляла желать лучшего — по сути, вторая версия Ext GWT полностью вытесняет все средства компоновки интерфейса GWT, предлагая собственные API для всего, вплоть до обработки событий. Поэтому для третьей версии библиотеки, доступной сейчас в виде developer preview, разработчики из Sencha активно переписывают скриптовое наследие Ext JS, используя паттерны и идиомы, принятые в GWT. Главное ожидаемое преимущество — более корректная интеграция с GWT и, как следствие, более компактный и оптимизированный код интерфейса: генерация и обфускация JavaScript и CSS происходит на этапе GWT-компиляции, с использованием механизма deferred binding, за счёт чего исключаются фрагменты кода, не использующиеся в текущем проекте.
Одним из паттернов, перенятых у GWT, стал Appearance, позволяющий выделить виджет, его разметку, стиль и связующую логику в независимые и заменяемые части. Такой подход позволяет легко менять стили и темы виджетов, и даже их разметку — например, использовать в качестве DOM-реализации кнопки либо div, либо button. Вот простейший пример применения паттерна Appearance для простой div-кнопки, у которой можно изменять текст и иконку:
Вложенный интерфейс Style представляет стиль отрисовки компонента. По умолчанию он связывается с внешним файлом DefaultAppearance.css — эта связь задаётся в интерфейсе Resources. Наконец, третий вложенный интерфейс, Template, представляет разметку компонента, и связан по умолчанию с внешним файлом DefaultAppearance.html. В конструкторе объекта DefaultAppearance можно переопределить реализацию интерфейса Resources, таким образом заменив стиль или тему компонента. Аналогичным образом можно поменять шаблон Template, или даже весь объект DefaultAppearance целиком — виджету достаточно знать простой интерфейс Appearance, чтобы делегировать ему установку параметров или обработку событий.
В итоге Ext GWT даёт нам три точки расширения, через которые мы можем повлиять на отображение виджета: изменив его CSS-стиль, HTML-разметку, либо полностью переопределив объект Appearance и механизм, с помощью которого он взаимодействует с DOM для отрисовки виджета. Благодаря паттерну Appearance все эти аспекты оказываются аккуратно отделёнными друг от друга.
Наконец-то можно будет отказаться от интерфейса ModelData, в который необходимо было раньше оборачивать любые JavaBean-объекты, чтобы они могли служить моделями данных. В Store и Loader возможно станет использовать любые объекты, не связанные какими-либо контрактами интерфейсов и не расширяющие специализированные классы. Всё это достигается за счёт магии deferred binding — код для доступа к конкретным полям объекта генерируется на этапе GWT-компиляции. Пока что примеры нового API для работы с хранилищами и загрузчиками отсутствуют, зато новшество продемонстрировано на примере другого нововведения — переработанного механизма шаблонов XTemplate:
Поля любого переданного объекта теперь можно использовать непосредственно в теле шаблона — так, в приведённом коде используется поле customer.name. Для тестирования этого кода мне не пришлось даже объявлять каких-либо мета-интерфейсов — всё необходимое генерируется магически на этапе компиляции. Сам шаблон, как видно из примера, может содержаться либо в строке-значении аннотации XTemplate, либо во внешнем файле, имя которого помещается в поле source той же аннотации. Результатом применения шаблона становится безопасный HTML — безопасный в том смысле, что полученный SafeHtml аккуратно обращается с возможными источниками XSS-атак в переданных полях и значениях.
Ещё одна давно ожидаемая функция — интеграция с существующим ещё с GWT 2.0 механизмом разметки интерфейса в XML — UiBinder. Он позволяет вынести во внешний XML-файл раскладку компонентов, сделав её декларативной и структурированной и отделив от логики интерфейса, прописанной в Java-коде. До сих пор UiBinder был доступен лишь для стандартных виджетов и контейнеров GWT, однако сейчас разработчики Ext GWT активно работают над интеграцией с ним собственных компонентов. Основной проблемой, из-за которой API ещё до конца не зафиксирован и даже вынесен в отдельный jar-файл, является сложность настройки контейнеров с помощью LayoutData — текущая реализация UiBinder не позволяет писать собственные парсеры для атрибутов XML-разметки. Разработчики Ext GWT и GWT сейчас занимаются согласованием изменений в GWT, которые сделают это возможным. Пока что предлагается следующий вариант (корневые теги для удобочитаемости опущены):
Легко видеть, что предлагаемый синтаксис поля layoutData чем-то напоминает CSS-стили в атрибутах. Для работы этого механизма необходимо подключить к проекту библиотеки gxt-uibinder-3.0.0-SNAPSHOT.jar и uibinder-bridge-2.3.0-SNAPSHOT.jar. Однако надо сказать, что у меня так и не получилось протестировать приведённый пример — GWT упорно отказывался преобразовывать неизвестное ему содержимое layoutData в объект BorderLayoutData.
К прочим мелким приятностям в новой версии библиотеки можно отнести:
В заключение отмечу, что текущая версия Ext GWT 3.0 работает в связке с GWT 2.3.0, и требует как минимум Java 1.6 для успешной компиляции, что обусловлено в том числе и требованиями самого GWT. Поменялся и корневой пакет библиотеки — в духе недавнего ребрендинга компании com.extjs был заменён на com.sencha. Разработчики обещают выпускать новые версии developer preview раз в несколько недель, однако отмечают, что эти версии пока лишь демонстрируют новые функции и ещё не зафиксированный API, а потому не рекомендуются для использования в разработке.
Однако интеграция Ext GWT и GWT до сих пор оставляла желать лучшего — по сути, вторая версия Ext GWT полностью вытесняет все средства компоновки интерфейса GWT, предлагая собственные API для всего, вплоть до обработки событий. Поэтому для третьей версии библиотеки, доступной сейчас в виде developer preview, разработчики из Sencha активно переписывают скриптовое наследие Ext JS, используя паттерны и идиомы, принятые в GWT. Главное ожидаемое преимущество — более корректная интеграция с GWT и, как следствие, более компактный и оптимизированный код интерфейса: генерация и обфускация JavaScript и CSS происходит на этапе GWT-компиляции, с использованием механизма deferred binding, за счёт чего исключаются фрагменты кода, не использующиеся в текущем проекте.
Реализация виджетов с использованием Appearance
Одним из паттернов, перенятых у GWT, стал Appearance, позволяющий выделить виджет, его разметку, стиль и связующую логику в независимые и заменяемые части. Такой подход позволяет легко менять стили и темы виджетов, и даже их разметку — например, использовать в качестве DOM-реализации кнопки либо div, либо button. Вот простейший пример применения паттерна Appearance для простой div-кнопки, у которой можно изменять текст и иконку:
public interface Appearance {
void render(SafeHtmlBuilder sb);
void onUpdateText(XElement parent, String text);
void onUpdateIcon(XElement parent, Image icon);
}
public static class DefaultAppearance implements Appearance {
public interface Style extends CssResource {
String testButton();
String testButtonText();
String testButtonImage();
}
public interface Resources extends ClientBundle {
@Source("DefaultAppearance.css")
Style style();
}
public interface Template extends XTemplates {
@XTemplate(source = "DefaultAppearance.html")
SafeHtml template(Style style);
}
private final Style style;
private final Template template;
public DefaultAppearance() {
this((Resources) GWT.create(Resources.class));
}
public DefaultAppearance(Resources resources) {
this.style = resources.style();
this.style.ensureInjected();
this.template = GWT.create(Template.class);
}
@Override
public void onUpdateIcon(XElement parent, Image icon) {
XElement element = parent.selectNode("." + style.testButtonImage());
element.removeChildren();
element.appendChild(icon.getElement());
}
@Override
public void onUpdateText(XElement parent, String text) {
parent.selectNode("." + style.testButtonText()).setInnerText(text);
}
@Override
public void render(SafeHtmlBuilder sb) {
sb.append(template.template(style));
}
}
Вложенный интерфейс Style представляет стиль отрисовки компонента. По умолчанию он связывается с внешним файлом DefaultAppearance.css — эта связь задаётся в интерфейсе Resources. Наконец, третий вложенный интерфейс, Template, представляет разметку компонента, и связан по умолчанию с внешним файлом DefaultAppearance.html. В конструкторе объекта DefaultAppearance можно переопределить реализацию интерфейса Resources, таким образом заменив стиль или тему компонента. Аналогичным образом можно поменять шаблон Template, или даже весь объект DefaultAppearance целиком — виджету достаточно знать простой интерфейс Appearance, чтобы делегировать ему установку параметров или обработку событий.
В итоге Ext GWT даёт нам три точки расширения, через которые мы можем повлиять на отображение виджета: изменив его CSS-стиль, HTML-разметку, либо полностью переопределив объект Appearance и механизм, с помощью которого он взаимодействует с DOM для отрисовки виджета. Благодаря паттерну Appearance все эти аспекты оказываются аккуратно отделёнными друг от друга.
JavaBean-объекты как модели данных
Наконец-то можно будет отказаться от интерфейса ModelData, в который необходимо было раньше оборачивать любые JavaBean-объекты, чтобы они могли служить моделями данных. В Store и Loader возможно станет использовать любые объекты, не связанные какими-либо контрактами интерфейсов и не расширяющие специализированные классы. Всё это достигается за счёт магии deferred binding — код для доступа к конкретным полям объекта генерируется на этапе GWT-компиляции. Пока что примеры нового API для работы с хранилищами и загрузчиками отсутствуют, зато новшество продемонстрировано на примере другого нововведения — переработанного механизма шаблонов XTemplate:
interface TemplateTest extends XTemplates {
@XTemplate("<div><span>{name}</span></div>")
SafeHtml renderCustomer(Customer customer);
@XTemplate(source="template.html")
SafeHtml renderCustomerExternal(Customer customer);
}
...
TemplateTest template = GWT.create(TemplateTest.class);
SafeHtml html = template.renderCustomer(customer);
Поля любого переданного объекта теперь можно использовать непосредственно в теле шаблона — так, в приведённом коде используется поле customer.name. Для тестирования этого кода мне не пришлось даже объявлять каких-либо мета-интерфейсов — всё необходимое генерируется магически на этапе компиляции. Сам шаблон, как видно из примера, может содержаться либо в строке-значении аннотации XTemplate, либо во внешнем файле, имя которого помещается в поле source той же аннотации. Результатом применения шаблона становится безопасный HTML — безопасный в том смысле, что полученный SafeHtml аккуратно обращается с возможными источниками XSS-атак в переданных полях и значениях.
UiBinder: декларативная раскладка интерфейса
Ещё одна давно ожидаемая функция — интеграция с существующим ещё с GWT 2.0 механизмом разметки интерфейса в XML — UiBinder. Он позволяет вынести во внешний XML-файл раскладку компонентов, сделав её декларативной и структурированной и отделив от логики интерфейса, прописанной в Java-коде. До сих пор UiBinder был доступен лишь для стандартных виджетов и контейнеров GWT, однако сейчас разработчики Ext GWT активно работают над интеграцией с ним собственных компонентов. Основной проблемой, из-за которой API ещё до конца не зафиксирован и даже вынесен в отдельный jar-файл, является сложность настройки контейнеров с помощью LayoutData — текущая реализация UiBinder не позволяет писать собственные парсеры для атрибутов XML-разметки. Разработчики Ext GWT и GWT сейчас занимаются согласованием изменений в GWT, которые сделают это возможным. Пока что предлагается следующий вариант (корневые теги для удобочитаемости опущены):
<border:BorderLayoutContainer pixelSize="800, 400">
<border:north layoutData="size:100; margins: 0 0 5 0">
<gxt:ContentPanel />
</border:north>
<border:west layoutData="size:150; margins: 0 5 0 0">
<gxt:ContentPanel />
</border:west>
<border:center layoutData="0">
<gxt:ContentPanel heading="BorderLayout UiBinder Example" />
</border:center>
<border:east layoutData="size:150; margins: 0 0 0 5">
<gxt:ContentPanel />
</border:east>
<border:south layoutData="size:100; margins: 5 0 0 0">
<gxt:ContentPanel />
</border:south>
</border:BorderLayoutContainer>
Легко видеть, что предлагаемый синтаксис поля layoutData чем-то напоминает CSS-стили в атрибутах. Для работы этого механизма необходимо подключить к проекту библиотеки gxt-uibinder-3.0.0-SNAPSHOT.jar и uibinder-bridge-2.3.0-SNAPSHOT.jar. Однако надо сказать, что у меня так и не получилось протестировать приведённый пример — GWT упорно отказывался преобразовывать неизвестное ему содержимое layoutData в объект BorderLayoutData.
Заключение
К прочим мелким приятностям в новой версии библиотеки можно отнести:
- унифицированный с GWT механизм обработки событий — никаких листенеров, стандартные хендлеры;
- переведённые с Flash на JavaScript графики — примеры из имеющегося в дистрибутиве приложения Ext GWT Explorer, как обычно, впечатляют вылизанностью и быстродействием;
- некоторые упрощения в контейнерах и раскладках — контейнеры LayoutContainer отныне жёстко связаны с раскладками Layout (RowLayoutContainer, BorderLayoutContainer, и т.д.), что несколько облегчает их конфигурацию и обеспечивает при этом типовую безопасность.
В заключение отмечу, что текущая версия Ext GWT 3.0 работает в связке с GWT 2.3.0, и требует как минимум Java 1.6 для успешной компиляции, что обусловлено в том числе и требованиями самого GWT. Поменялся и корневой пакет библиотеки — в духе недавнего ребрендинга компании com.extjs был заменён на com.sencha. Разработчики обещают выпускать новые версии developer preview раз в несколько недель, однако отмечают, что эти версии пока лишь демонстрируют новые функции и ещё не зафиксированный API, а потому не рекомендуются для использования в разработке.
Ссылки
- Ext GWT 3.0 Appearance Design
- Ext GWT 3.0 XTemplate Redesign
- Ext GWT 3.0 Developer Preview 1
- Ext GWT 3.0 Developer Preview 2