GWTP
GOO-TEEPEE
By: Ben Bracha
Agenda
Background: Main concepts of GWT GWTP key concepts and features
GWT Main concepts
Java -> JS Deferred binding Code splitting (Gin/ Guice)
Java -> JS
Build complex browser-based apps in Java
Java javascript cross compiler All the goodies of Java
Rock-solid IDEs and tools Refactoring Unit testing
Deferred Binding
Generating many version of the code in compile time Only one is being loaded by particular client in run time Each version is generated per browser along with other axis defined by the application (browser, locale) In essence, this is the GWT answer for java reflection GWT.Create(SomeType.class) Code generation / replacement (for example RavelloGrey.gwt.xml)
Code Splitting
As application grows JS code downloading may take some time GWT let us split our code download what you need, when you need it GWT.runAsync defines our split point GWTP makes this clear for us using presenterproxy (TBD)
http://code.google.com/webtoolkit/doc/latest/Dev GuideCodeSplitting.html
Gin & Guice
Guice is a dependency injection (DI) framework written by Google Inversion of control (IOC) dependencies for a component are set to it externally Less usage of the new keyword Can replace actual dependencies easily (for testing..) The Injector, injector configuration Gin Guice written for GWT (lack of reflection ) http://code.google.com/p/google-guice/ http://code.google.com/p/google-gin/
Gin & Guice
*.gwt.xml: <module> ... <inherits name="com.google.gwt.inject.Inject"/> ... </module> Gin module: public class MyWidgetClientModule extends AbstractGinModule { protected void configure() { bind(SomeInterface.class).in(SomeImplementation.class); bind(MyWidgetMainPanel.class).in(Singleton.class); bind(MyRemoteService.class).toProvider(MyRemoteServiceProvider.class); } } Ginjector: [usually for top level components only] @GinModules(MyWidgetClientModule.class) public interface MyWidgetGinjector extends Ginjector { MyWidgetMainPanel getMainPanel(); }
Gin & Guice
@Inject On members and on Constructor (note that members are injected only after construction!) @ImplementedBy annotation
@ImplementedBy(PayPalCreditCardProcessor.class) public interface CreditCardProcessor { } Equivalent to: bind(CreditCardProcessor.class).to(PayPalCreditCardProcessor.class);
Singleton scope
bind(TransactionLog.class).to(InMemoryTransactionLog.class).in (Singleton.class); (default is not singleton!)
GWTP
MVP framework for building GWT applications
MVP support (presenters, presenter-widgets, nested presenters) Places (history and bookmark support) Command pattern (The dispatcher) The event bus
http://code.google.com/p/gwt-platform/
MVP
Model (entity graph) View (dumb, no logic) Presenter (logic, no UI code) Advantages
Testability Low coupling
Presenter - Proxy- View
View responsible for the widgets layout on screen Presenter Contains the business logic for a view (loading data from server, handling events..) Proxy help getting the benefits of codesplitting. If requested presenter and view will be behind a split point Binding all together (they are all singletons!!) in AbstractPresenterModule:
bindPresenter(LoginPresenter.class, LoginPresenter.MyView.class, LoginView.class, LoginPresenter.MyProxy.class);
Presenter View interactions
Interactions should be made only through interfaces The presenter defines an inner view interface (extends HasUiHandlers) implemented by the view An external interface extends UiHandlers defines operations required by the view. This is implemented by the presenter On presenter construction: getView().setUiHandlers(this);
Presenter View interactions (supervising controller pattern)
public interface UserProfileUiHandlers extends UiHandlers{ void onSave(); }
public class UserProfilePresenter extends Presenter<UserProfilePresenter.MyView, UserProfilePresenter.MyProxy> implements UserProfileUiHandlers{ public interface MyView extends View, HasUiHandlers<UserProfileUiHandlers>{ }
@Inject ExamplePresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) { super(eventBus, view, proxy); getView().setUiHandlers(this); } ...
public class ExampleView extends ViewWithUiHandlers<UserProfileUiHandlers> implements MyView { ...
@UiHandler("saveButton") void onSaveButtonClicked(ClickEvent event) { if (getUiHandlers() != null) { getUiHandlers().onSave(); } }
Presenter Vs. PresenterWidget
Regular presenter-proxy-view tuple is singleton as defined by the framework What if we have some complex UI component that required presenter-view but not necessarily singleton? Use PresenterWidget-View pair
PresenterWidget has no proxy It is up to its hosting parent to instantiate it (inject!) and reveal it (add-to-slot)
Presenters Lifecycle
Phase When Called What to do
onBind() onUnbind()
onReveal() onHide() onReset()
After presenter construction Not called automatically
Just before presenter revealed Just before presenter becomes hidden when navigation occurs and the presenter is still visible after
Add handlers to view Undo everything from bind()
Any initialization or action should be done before reveal Clean any initialization you did in onReveal() The most commonly used. This is a good place to refresh presenters data
Never call lifecycle methods explicitly Always call super() when overriding
Places
A place is usually a page in the application which the user can navigate to A place attached to some presenter The PlaceManager is responsible for all place operations A place can contain state parameters (URL) for bookmarking, history
We can add navigation confirmation We can set up a default page for the application We can set up a default error-page for the application
Places
http://phone.com#!search;q=iphone
public class SearchPresenter { @ProxyStandard @NameToken("!Search") public interface MyProxy extends ProxyPlace<SearchPresenter>{} @Override public void prepareFromRequest(PlaceRequest request) { } String type = request.getParameter(); }
* When preparing from request, If your presenter needs to fetch data from server that is required for it to become visible, you can block the reveal-phase and use manual-reveal
Nested Presenters
You can build a composite-presenter made up from several child-presenters
Imply some common layout Better design (break complex structures) Reuse (presenters widgets)
Composition is done with slots
Presenter defines named slots View handles slot layout The presenter may or may not know his children
http://code.google.com/p/gwt-platform/wiki/SimpleNestedSample
Nested Presenters
public class MainPagePresenter extends Presenter<MainPagePresenter.MyView, MainPagePresenter.MyProxy> { @ContentSlot public static final Type<RevealContentHandler<?>> TYPE_SetMainContent = new Type<RevealContentHandler<?>>(); public interface MyView extends View {} @Override protected void revealInParent() { RevealRootContentEvent.fire(this, this); } }
Nested Presenter
public class MainPageView extends ViewImpl implements MyView { @UiField FlowPanel mainContentPanel; @Override public void setInSlot(Object slot, Widget content) { if (slot == MainPagePresenter.TYPE_SetMainContent) { setMainContent(content); } else { super.setInSlot(slot, content); } } private void setMainContent(Widget content) { mainContentPanel.clear(); if (content != null) { mainContentPanel.add(content); } } }
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> <ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder > <g:HTMLPanel> <npui:MainMenu /> <g:FlowPanel ui:field="mainContentPanel" /> <npui:MainMenu /> </g:HTMLPanel> </ui:UiBinder>
Nested Presenters
public class HomePresenter extends Presenter<HomePresenter.MyView, HomePresenter.MyProxy> { public interface MyView extends View {} @ProxyCodeSplit @NameToken(NameTokens.homePage) public interface MyProxy extends ProxyPlace<HomePresenter> {} @Inject public HomePresenter( final EventBus eventBus, final MyView view, final MyProxy proxy) { super(eventBus, view, proxy); } @Override protected void revealInParent() { RevealContentEvent.fire(this, MainPagePresenter.TYPE_SetMainContent, this); }
The Dispatcher
Centralized component handling all client-server communication Command pattern
Action: shared with the server and client. Presents some logic action that can be done by the client Result: shared with the server and client. Each action bounded to exactly one result, presenting the return value of the action. Handler: known only to the server. Handles an actionresult pair.
http://code.google.com/p/gwt-platform/wiki/GettingStartedDispatch
The Dispatcher
Action:
public class CreateNewDesignAction extends UnsecuredActionImpl<CreateNewDesignResult> {
private String designName; ...
Result:
public class CreateNewDesignResult implements Result { private DesignDto designDto; }
Bind server handler:
public class ServerModule extends HandlerModule { @Override protected void configureHandlers() { bindHandler(CreateNewDesignAction.class, CreateNewDesignServerActionHandler.class); }
Invoke action from client:
dispatcher.execute(new CreateNewDesignAction(text), new BaseCallback<CreateNewDesignResult>() { @Override public void onSuccess(CreateNewDesignResult result) { setCurrentDesign(result.getDesignDto()); { });
The Event Bus
Central unit for communication between loosely related objects (vs. direct method invocation) Use it for notifying the world (the application) about interesting events (for example: entity updated) Not over-use it avoid chatty application (performance) Can used private event-bus \ named-event-bus (for separation)
http://arcbees.wordpress.com/2010/08/24/gwt-platform-event-bestpractice/
The Event Bus
The Event Bus