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