architecting your gwt applications with gwt-platform - lesson 02

Post on 29-Nov-2014

13.295 Views

Category:

Documents

8 Downloads

Preview:

Click to see full reader

DESCRIPTION

Write GXT3 application using GWTP

TRANSCRIPT

Architecting your GWT application with GWT-

Platform

Juan Manuel Rojas R.http://rhemsolutions.com/ Lima, Perú 26/10/2012

Architecting your GWT application

Google found that a Model-view-presenter (MVP) architecture works best when developing GWT appshttps://developers.google.com/web-toolkit/articles/mvp-architecture

Model View Presenter FrameworksMvp4ggwt-dispatchgwt-platform

GWT-Platform

It's an open source MVP framework, that supports many nice features of GWT, including code splitting and history management, with a simple annotation-based API.

A highly productive app stack

http://www.youtube.com/watch?feature=player_detailpage&v=imiquTOLl64#t=200s

gwt-platform (GWTP)

GWT RequestFactory

JPA 2.0 - PostgreSQL

UI (MVP)

RPC

PostgreSQL

Google Plugin for Eclipse

Update sites

Eclipse 4.2 (Juno)http://dl.google.com/eclipse/plugin/4.2

GWTP Eclipse Plugin

To install the GWTP Eclipse Plugin, from Eclipse go to Help > Install New Software

Click on Add...Name: GWTPLocation: http://plugin.gwt-platform.googlecode.com/hg/update

Clean example code1. Delete theme lines from Module.gwt.xml2. Delete files from client package GreetingService GreetingServiceAsync server package, GreetingServiceImpl shared packaged FieldVerifier src/test/java GwtTestFirstProject src/test/resources FirstProjectJUnit.gwt.xml3. Clean Css file and html table code src/main/webapp4. Delete servlet definition from web.xml src/main/webapp/WEB-INF

Entry point

package com.example.client;

import com.google.gwt.core.client.EntryPoint;import com.google.gwt.user.client.Window;

public class FirstProject implements EntryPoint { public void onModuleLoad() { Window.alert("empty project"); }}

Dependency injection (DI)

Separate behavior from dependency resolution.

Injects the dependent element (object or value etc) to the destination automatically.

Reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component.http://en.wikipedia.org/wiki/Dependency_injection

Dependency injection for GWT

google-ginGIN is built on top of Guice and uses (a subset of) Guice's binding language.

<!-- google-gin --><dependency>

<groupId>com.google.gwt.inject</groupId> <artifactId>gin</artifactId> <version>2.0.0</version> <exclusions> <exclusion> <groupId>com.google.gwt</groupId> <artifactId>gwt-servlet</artifactId> </exclusion> </exclusions>

</dependency>

Dependency injection for Java 5

google-guiceThink of Guice's @Inject as the new new.Guice embraces Java's type safe nature using generics and annotations. <!-- google guice --> <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>3.0</version> </dependency>

Dependency injection GIN

Constructor injection

public class ClientPlaceManager {

@Injectpublic ClientPlaceManager(final EventBus eventBus,

final TokenFormatter tokenFormatter) { //eventBus and tokenFormatter are not null

}}

public class ColaboradorPresenter {

@Inject Messages messages;

public ColaboradorPresenter() {//messages is null}

protected void onBind() {//messages is not null}

}

Field injection

Dependency injection GIN (Continuation)@Provides MethodsWhen you need create and initialize an object.

public class ClientModule extends AbstractPresenterModule {

@Provides @Singleton public AppRequestFactory createRequestFactory(EventBus eventBus, MyDefaultRequestTransport requestTransport) {

AppRequestFactory factory = GWT.create(AppRequestFactory.class); factory.initialize(eventBus, requestTransport); return factory; }}

public class ColaboradorPresenter { private final AppRequestFactory factory; @Inject public ColaboradorPresenter(Provider<AppRequestFactory> provider){ factory = provider.get(); }}

How to inject

Setting GWTP and GXT3

Add to your Module.gwt.xml

<!-- sencha gxt3 --><dependency> <groupId>com.sencha.gxt</groupId> <artifactId>gxt</artifactId> <version>3.0.1</version> </dependency>

<inherits name='com.sencha.gxt.ui.GXT'/> <inherits name='com.gwtplatform.mvp.Mvp'/>

Add to your POM.xml

<properties> ... <gwtp.version>0.7</gwtp.version> ... </properties>

<!-- MVP component --> <dependency> <groupId>com.gwtplatform</groupId> <artifactId>gwtp-mvp-client</artifactId> <version>${gwtp.version}</version> <scope>provided</scope> </dependency> <!-- Annotation component --> <dependency> <groupId>com.gwtplatform</groupId> <artifactId>gwtp-processors</artifactId> <version>${gwtp.version}</version> <scope>provided</scope> </dependency>

Setting GWTP Create the directoriescom.example.client |--presenter |--view |--place

import com.google.web.bindery.event.shared.EventBus;

public class ClientPlaceManager extends PlaceManagerImpl {

private final PlaceRequest defaultPlaceRequest;

@Injectpublic ClientPlaceManager(final EventBus eventBus,

final TokenFormatter tokenFormatter,@DefaultPlace final String defaultPlaceNameToken) {

super(eventBus, tokenFormatter);this.defaultPlaceRequest = new PlaceRequest(defaultPlaceNameToken);

}

public void revealDefaultPlace() {revealPlace(defaultPlaceRequest, false);

}}

import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.ElementType.PARAMETER;import static java.lang.annotation.ElementType.METHOD;import static java.lang.annotation.RetentionPolicy.RUNTIME;

@BindingAnnotation@Target({ FIELD, PARAMETER, METHOD })@Retention(RUNTIME)public @interface DefaultPlace {}

public class NameTokens {

}

Setting GIN for GWTP

Create a gin package in the client side

public class ClientModule extends AbstractPresenterModule {

@Overrideprotected void configure() { install(new DefaultModule(ClientPlaceManager.class));}

}

import com.google.web.bindery.event.shared.EventBus;@GinModules(ClientModule.class)public interface ClientGinjector extends Ginjector { EventBus getEventBus(); PlaceManager getPlaceManager();}

Getters methods for get objects

Define objects available for injection

If GIN can't find a binding for a class, it falls back to calling GWT.create() on that class.

Inheriting the GIN module

<inherits name="com.google.gwt.inject.Inject"/>

Setting GIN for GWTP(Continuation)Configure your EntryPoint

public class FirstProject implements EntryPoint { private final ClientGinjector ginjector = GWT.create(ClientGinjector.class);

public void onModuleLoad() { // This is required for Gwt-Platform proxy's generatorDelayedBindRegistry.bind(ginjector);

ginjector.getPlaceManager().revealCurrentPlace(); }}

Add the next properties to your Module.gwt.xml<module> ... <define-configuration-property name='gin.ginjector' is-multi-valued='false' /> <set-configuration-property name='gin.ginjector' value='com.example.client.gin.ClientGinjector' />

</module>

GWT-Platform and GXT3

Structure your views

A place represent a url of a program

GWT-Platform and GXT3(Continuation)

TokensMost presenters have a token so the user can navigate.

When there is not token the default presenter is shown.You can navigate from one presenter to another by:● Typing another url● Using a Hyperlink Widget● Using a PlaceRequest

PlaceRequest request = new PlaceRequest("inicio");

placeManager.revealPlace(request);

GWT-Platform and GXT3(Continuation)

Nested PresentersYour header, footer and menu are in one presenter the "parent presenter".

GWT-Platform and GXT3(Continuation)

Presenter Widget Is for reusable graphical and logical code.

GWT-Platform and GXT3(Continuation)

All presenters are composed of three files if you will use UIBinder

All presenters have a inner interface that view implements.

Presenter lifecycle

GWT-Platform and GXT3(Continuation)

GWT-Platform and GXT3(Continuation)

Presenter lifecycle

1. Presenters are Singleton so it is instantiated once.2. prepareFromRequest(): Is where you can get url

parameters.3. onBind(): Is called when the presenter is instantiated.4. onReveal(): Is called whenever the Presenter was not

visible on screen and becomes visible.5. onHide(): Is called whenever the Presenter was visible

on screen and is being hidden.6. onReset(): Is called whenever the user navigates to a

page that shows the presenter, whether it was visible or not.

Create Presenterusing GWTP Eclipse Plugin

First PresenterLayoutPresenterOptions

● Use UiBinder● RevealRootContentEvent● Is Not a Place

OrderingMove to view packageLayoutView.javaLayoutView.ui.xml

First Presenter LayoutPresenter(Continuation)

Deprecated shared.EventBus

GWTP Eclipse Plugin use EventBus from event.shared next this package was deprecated and change to web.bindery.event.shared.

* Is important to do the next in every presenter you create with GWTP Eclipse Plugin

Delete import com.google.gwt.event.shared.EventBusReplace byimport com.google.web.bindery.event.shared.EventBus;

First Presenter LayoutPresenter(Continuation)Add the next field to LayoutPresenter

import com.google.gwt.event.shared.GwtEvent.Type;//for nested presenters@ContentSlot public static final Type<RevealContentHandler<?>> SLOT_content = new Type<RevealContentHandler<?>>();

Modify LayoutView.ui.xml<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'xmlns:g='urn:import:com.google.gwt.user.client.ui'xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"xmlns:container="urn:import:com.sencha.gxt.widget.core.client.

container" ><container:Viewport> <!--for nested presenter --> <gxt:ContentPanel ui:field="contentPanel" borders="false"

headerVisible="false" bodyBorder="false" /> </container:Viewport>

</ui:UiBinder>

First Presenter LayoutPresenter(Continuation)

Setting Vertical Layout (LayoutView.ui.xml)<ui:UiBinder> ... <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="minVerticalLayoutData" > <ui:attributes width="1" height="-1" /> </ui:with> <ui:with type="com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData" field="maxVerticalLayoutData" > <ui:attributes width="1" height="1" /> </ui:with>

<container:Viewport> ...</container:Viewport>

</ui:UiBinder>

<ui:UiBinder> ...

<container:Viewport> <container:VerticalLayoutContainer> <container:child layoutData="{minVerticalLayoutData}"> <g:HTMLPanel> <div> <div>First Project</div> </div> </g:HTMLPanel> </container:child> <container:child layoutData="{maxVerticalLayoutData}"> <!--for nested presenter --> <gxt:ContentPanel ui:field="contentPanel" borders="false" headerVisible="false"

bodyBorder="false" /> </container:child> </container:VerticalLayoutContainer></container:Viewport>

</ui:UiBinder>

First Presenter LayoutPresenter(Continuation)

Using Vertical Layout (LayoutView.ui.xml)

First Presenter LayoutPresenter(Continuation)

@UiField ContentPanel contentPanel;//for nested presenter

Add the next field to LayoutView

@Overridepublic void setInSlot(Object slot, Widget content) {

if(slot == LayoutPresenter.SLOT_content){contentPanel.clear();if(content != null){

contentPanel.add(content);contentPanel.forceLayout();

}return;

}super.setInSlot(slot, content);

}

Add the next method to LayoutView

Default PresenterWelcomePresenterOptions

● Use UiBinder● RevealContentEvent● Content Slot

(from parent presenter)● Is a Place● CodeSplit (recommended)● Token: #welcome● DefaultPlace

Default Presenter WelcomePresenter(Continuation)

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'xmlns:g='urn:import:com.google.gwt.user.client.ui'xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container"

>

<gxt:ContentPanel headingText="Welcome" > <container:CenterLayoutContainer> <g:HTMLPanel><h2> Welcome User</h2></g:HTMLPanel> </container:CenterLayoutContainer></gxt:ContentPanel>

</ui:UiBinder>

Modify WelcomeView.ui.xml

reset.css

Download reset.cssput this css in your HTML header<head>...<link type="text/css" rel="stylesheet" href="reset.css">...</head>

In your Module.css file* { font-family: arial, helvetica, tahoma, sans-serif;}

mvn gwt:run

Styling our application(Continuation)public interface Resources extends ClientBundle {

@Source("add.png") ImageResource add();

@Source("delete.png") ImageResource delete();

@Source("cancel.png") ImageResource cancel();

@Source("edit.png") ImageResource edit();

@Source("save.png") ImageResource save();

@Source("exit.png") ImageResource exit();

}

client.resources |- Resources.java

Styling our application(Continuation)public interface CommonsCss extends CssResource {

@ClassName("layout-header")String layout_header();

@ClassName("layout-title")String layout_title();

String logout();

@ClassName("underline")String underline();

@ClassName("button")String button();

String exit();}

client.resources |- CommonsCss.java |- Commons.css

Styling our application(Continuation)

Add to your ClientBundle

public interface Resources extends ClientBundle {

...

@Source("bg.gif") ImageResource bg();

@Source("Commons.css") CommonsCss commonsCss();

}

Styling our application(Continuation)

Modify LayoutView.ui.xml<ui:UiBinder> <ui:with type="com.example.client.resources.Resources" field="resources" />

...

<container:child layoutData="{minVerticalLayoutData}"> <g:HTMLPanel styleName="{resources.commonsCss.layout_header}"> <div class="{resources.commonsCss.layout_title}"> First Project </div> <div class="{resources.commonsCss.logout}"> <span class="{resources.commonsCss.underline}"> <a class="{resources.commonsCss.button}" href="/logout" > <span class="{resources.commonsCss.exit}">Logout</span> </a> </span> </div> </g:HTMLPanel> </container:child> ...

</ui:UiBinder>

Styling our application(Continuation)

Modify LayoutView.javapublic class LayoutView extends ViewImpl implements LayoutPresenter.MyView {

...

@Injectpublic LayoutView(final Binder binder, final Resources resources ) {

resources.commonsCss().ensureInjected();//load css

widget = binder.createAndBindUi(this);}

}

mvn gwt:run

Thanks

top related