воскресенье, 29 июля 2012 г.

Enumeration ListBox with GWT Editor framework

       Представим следующую ситуацию: вы разрабатываете GWT приложение и ваши сущности(domain entities - основные структурные строительные блоки любой системы) имеют поля в виде перечислений(Enum). Ваши Proxies/DTO также работают с полями в виде перечислений. Но когда данные приходят на клиентскую часть(UI), то виджет ListBox работает только со строковым(String) форматом и необходимо выполнить преобразования из enum в String, и наоборот из String в enum. Но было бы хорошо иметь такой виджет, который бы умел работать с enum как со строками. Также добавим виджету возможность связывания данных между бином и UI-полями. Это возможность доступна благодаря GWT Editor framework
     Для начала создадим список перечислений возможных значений статуса:
  1. public enum Status {
  2.     PROGRESS,
  3.     RESOLVED,
  4.     REOPENED,
  5.     CLOSED,
  6.     OPEN;
  7.     
  8.     @Override
  9.     public String toString() {
  10.         return name().toLowerCase();
  11.     }
  12. }
* This source code was highlighted with Source Code Highlighter.
     Создадим ListBox умеющий работать с перечислением как со строками. Для этого наследуем класс от ValueListBox, который реализует интрефейс IsEditor.
  1. public class EnumerationListBox<E extends Enum<E>> extends ValueListBox<E> {
  2.     public EnumerationListBox() {
  3.         super(new EnumerationRenderer<E>(), new EnumerationKeyProvider<E>());
  4.     }
  5.     
  6.     public EnumerationListBox(Class<E> clazz) {
  7.         this();
  8.         setAcceptableValues(Arrays.asList(clazz.getEnumConstants()));
  9.     }
  10. }
* This source code was highlighted with Source Code Highlighter.
     Также нам необходимо создать классы EnumerationRenderer(отвечает за отрисовку объекта определенного типа в строковой форме) и EnumerationKeyProvider(обеспечивает ключ для элемента списка).
  1. public class EnumerationRenderer<E extends Enum<E>> implements Renderer<E> {
  2.  
  3.     @Override
  4.     public String render(E object) {
  5.         return object == null ? "" : object.toString();
  6.     }
  7.     
  8.     @Override
  9.     public void render(E object, Appendable appendable) throws IOException {
  10.         appendable.append(render(object));
  11.     }
  12. }
* This source code was highlighted with Source Code Highlighter.
  1. public class EnumerationKeyProvider<E extends Enum<E>> implements ProvidesKey<E> {
  2.  
  3.     @Override
  4.     public Object getKey(E item) {
  5.         return item == null ? null : item.name() ;
  6.     }
  7. }
* This source code was highlighted with Source Code Highlighter.
     Теперь когда у нас есть enumeration список, создадим для него контейнер согласно Editor контракту.
  1. public class TaskEditor extends Composite implements Editor<TaskProxy> {
  2.     
  3.     @UiField(provided = true)
  4.     EnumerationListBox<Status> priority = new EnumerationListBox<Status>(Status.class);
  5.     
  6.     public TaskEditor() {
  7.         initWidget(priority);
  8.         
  9.         priority.addValueChangeHandler(new ValueChangeHandler<Status>() {
  10.             @Override
  11.             public void onValueChange(ValueChangeEvent<Status> event) {
  12.                 // and you can do whatever you want with selected event.getValue()
  13.             }
  14.         });
  15.     }
  16. }
* This source code was highlighted with Source Code Highlighter.
     Добавим сюда механизм RequestFactory, который позволяет реализовать уровень доступа к данным на клиенте и сервере. Это позволяет структурировать серверный код в data-ориентированном виде и обеспечивает более высокий уровень абстракции чем GWT-RPC. Первоначально он был создан для CRUD операций на сущности, но позже добавили возможность использования как универсальный механизм RPC.
     Создадим интерфейс TaskProxy для сущности Task и определим необходимые get/set методы. Все методы отображаются(mapped) по именованному соглашению, т.е. имена должны быть идентичными.
  1. @ProxyFor(value = Task.class)
  2. public interface TaskProxy extends EntityProxy {
  3.     
  4.     Status getPriority();
  5.     public void setPriority(Status priority);
  6. }
* This source code was highlighted with Source Code Highlighter.
  1. public class Task {
  2.     @NotNull private Status priority;
  3.     
  4.     public Status getPriority() {
  5.         return priority;
  6.     }
  7.     
  8.     public void setPriority(Status priority) {
  9.         this.priority = priority;
  10.     }
  11. }
* This source code was highlighted with Source Code Highlighter.
     Создание полного client/server-примера, где будут разные Entity, DAO, Proxy, Locator, RequestFactory, RequestContext, Manager-class это уже дело отдельной статьи. А на этом все.

воскресенье, 22 июля 2012 г.

Programmatically fire events in GWT

     Иногда необходимо программно сгенерировать событие на определенном элементе, например на ссылке, метке или кнопке. Вызовем событие клика на ссылке(firstAnchor) из другой ссылки(secondAnchor). Рассмотрим возможные способы как это сделать.
1-ый способ:
  1. firstAnchor.addClickHandler(new ClickHandler() {
  2.     @Override
  3.     public void onClick(ClickEvent event) {
  4.         secondAnchor.fireEvent(new GwtEvent<ClickHandler>(){
  5.             @Override
  6.             public GwtEvent.Type<ClickHandler> getAssociatedType() {
  7.                 return ClickEvent.getType();
  8.             }
  9.             @Override
  10.             protected void dispatch(ClickHandler handler) {
  11.                 handler.onClick(null);
  12.             }
  13.         });
  14.     }
  15. });
* This source code was highlighted with Source Code Highlighter.
2-ой способ:
  1. NativeEvent nativeClickEvent =
  2.     Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
  3. DomEvent.fireNativeEvent(nativeClickEvent, secondAnchor);
  4. // or we can create ENTER keydown event
  5. NativeEvent nativeKeyDownEvent =
  6.     Document.get().createKeyDownEvent(false, false, false, false, KeyCodes.KEY_ENTER);
  7. DomEvent.fireNativeEvent(nativeKeyDownEvent, secondAnchor);
* This source code was highlighted with Source Code Highlighter.
3) следующий способ работает, но это небольшой хак :) потому что com.google.gwt.event.dom.client.ClickEvent имеет protected конструктор.
  1. // it works but, it's a little bit hack
  2. secondAnchor.fireEvent(new ClickEvent(){});
* This source code was highlighted with Source Code Highlighter.
4) так как все браузеры реализуют метод click() на элементе, который генерирует событие нажатия на элементе, то можно воспользоваться вызовом через JSNI
  1. click(secondAnchor.getElement());
  2. ...
  3. public static native void click(Element elem) /*-{
  4.     elem.click();
  5. }-*/;
* This source code was highlighted with Source Code Highlighter.
5) или можно скастовать элемент Element в ButtonElement или InputElement и вызвать метод click()
secondAnchor.getElement().<ButtonElement>cast().click();

* This source code was highlighted with Source Code Highlighter.