web-development using gwt + mvp4g
DESCRIPTION
Slides for the lecture at ADDConf (http://addconf.ru) 2011TRANSCRIPT
GWT + mvp4g
Anthony Kotenko, iPark ventures™2011 ©
web-development using
This presentation was specially prepared forApplication Developer Days 2011
Saint-Petersburg, Russia
goo.gl/4GgnS
Slides — there
[ PDF, ~2MB ]
Anthony Kotenko6 years in Java EE development
6 years in UI development
Sex: male
Anthony Kotenko6 years in Java EE development
6 years in UI development
Sex: male
http://shamansir.madfire.net
http://zokotuhaFly.habrahabr.ru
http://twitter.com/shaman_sir
http://profiles.google.com/shaman.sir
Schedule
1. Introduction. A brief history and examples of GWT usage
2. A brief introduction to concepts: - MVP / Reverse MVP - EventBus - Dependency Injection
3. Description of mvp4g framework - differences in RMVP implementation from GWT - difference in implementation of EventBus - multimoduleness - HistoryConverter conception - advantages / disadvantages
6. Working with non-Java Server-Side-API (versus RPC-services) - making call chains - callback vs. GwtEvent - advantages / disadvantages
4. Components in GWT - UiBinder, standard components - custom widgets development
7. i18n in GWT
5. Our layouting system development
8. Conclusion. Links to the examples
Your questions may (and must be) asked during the lecture:to let reporter know about you having question regarding the theme or not,
just raise your hand (any of them).
Like so:
Discussion is important
I have a question!
1. Briefly 'bout GWT
GWT
code.google.com/webtoolkit/
GoogleWebToolkit
/ˈɡwɪt/
GWT
/ˈɡwɪt/
/ˈɡwɪt/
Used in these projects:
Google Wave wave.google.com
Google Checkout checkout.google.com
Google Moderator google.com/moderator
Whirled whirled.com
Lombardi Blueprint blueprint.lombardi.com
ContactOffice beta.contactoffice.com
Used in these projects:
GoGrid gogrid.com
Curriki curriki.org
OpenKM openkm.com
Kdice kdice.com
SeeMap seemap.ru
Одноклассники odnoklassniki.ru
A box of useful tools
A box of useful toolsThe most complete
for web-developer
widgets
http://old.ongwt.com/public/WindowsLiveWriter_GWTMosaicnicewidgetlibrary_D777_image_2.png
widgetsoptimization
http://radar.oreilly.com/200912081729.jpg
widgets
cross-browserly!optimization
http://2.bp.blogspot.com/_VzXmgKXrn6Y/TKB2a3eZTBI/AAAAAAAAAaQ/mn7NmabwO8U/s1600/browsers%5Btsksoft.blogspot.com%5D.jpg
widgets
cross-browserly!
on-the-fly development
optimization
http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
widgets
cross-browserly!
on-the-fly development
optimization
OOP benefits
widgets
cross-browserly!
on-the-fly development
optimization
OOP benefits debug
http://www.ibm.com/developerworks/library/j-ajax4/eclipse-debug.jpg
widgets
cross-browserly!
on-the-fly development
optimization
OOP benefits debug
RPCAPI/DB
widgets
cross-browserly!
simple i18n
on-the-fly development
optimization
OOP benefits debug
RPC
http://test.ical.ly/wp-content/uploads/2010/04/i18n.png
widgets
cross-browserly!
simple i18n
on-the-fly development
optimization
OOP benefits
Code Splitting
debug
RPC
widgets
cross-browserly!
simple i18n
on-the-fly development
optimization
OOP benefits
Code Splitting
debug
RPC
http://www.safetylca.org/images/toolbox.jpg
http://blog.ericlamb.net/wp-content/uploads/2009/08/toolbox.jpghttp://www.safetylca.org/images/toolbox.jpg
GWT is a box of useful tools/ˈɡwɪt/
History
Version 1.0was released in 2006
Version 1.0was released in 2006
Version 1.6Google Eclipse Plugin
Project structure matchesWeb Application specification
Version 2.0 - Development Mode - Code Splitting - Declarative UI - Client Bundle
Version 2.1 MVP-conception RequestFactory / Editors
Version 2.1 MVP-conception RequestFactory / Editors
Version 2.2 UI Designer HTML5 Canvas support only Java 1.6 was left
Quake 2 in browser
https://y2mzuw.blu.livefilestore.com/y1m9Pc7Xi6qtMdKU_hGCv7VZrEKiIZTYqDIC5laL9A0LxracK6AN8EAet90MRtWlBJISphNp_Y8QCxpb0p9v1ylTCdwATCweUs9496DC14_ijBjhnpj2oRWme9B1SC2C0t9HJ1wX8RTsVQAhyDYCsqfeg/quake-html5-04-02-2010.jpg
quake2-gwt-port.appspot.com
Quake 2 in browserquake2-gwt-port.appspot.com
TrendyYouth
Modern
GWT is actively developing project, however it already contains everything you need
/ˈɡwɪt/
2. GWT Conceptions
almaer.com/blog/rotating-java-and-javascript-on-the-server
Any GWT-project starts from entry point
EntryPoint
● Java → JavaScript, JSNI● Development Mode● Code Splitting●<Module>.gwt.xml● MVC, MVP, RMVP, EventBus● Deferred Binding● Dependency Injection● Remote Service● JUnit● Disadvantages and remarks
JavaScript Logo is from marakana.com
JSNI
JavaScript Native Interface
JSNI
public native static void <functionName>(<parameters>) /*-{ . . . @<path.to.the.package>.<ClassName>::<methodName>( /L<param-type>;/L<param-type>;...)(<arguments>);
. . .
}-*/;
JSNI
public native static void getJson(int requestId, String url, StockWatcher handler) /*-{ var callback = "callback" + requestId; var script = document.createElement("script"); script.setAttribute("src", url+callback); script.setAttribute("type", "text/javascript");
window[callback] = function(jsonObj) { [email protected] .client.StockWatcher::handleJsonResponse( Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj); window[callback + "done"] = true; } setTimeout(function() { if (!window[callback + "done"]) { [email protected] .client.StockWatcher::handleJsonResponse( Lcom/google/gwt/core/client/JavaScriptObject;)(null); }
document.body.removeChild(script); delete window[callback]; delete window[callback + "done"]; }, 1000); document.body.appendChild(script);}-*/;
JSNI
You can wrap native JavaScript widgets with JSNI.For example, Google Maps or any WYSIWYG-editor
JSNI
WYSIWYG-widget,written in Closure
and integrated with the help of JSNIGoogle Maps widget,
integrated with the help of JSNI
Flash-objectusing JavaScript-callbacks,
which are called through JSNI
You can use JSNI tomake third-party components
written in JavaScriptbecome GWT-widgets
Development Mode
♥
Development Modehttp://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
♥
Development Modehttp://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
(but not for Opera)the plugin for your lovely browser
the plugin for your lovely IDE
♥
Development Modehttp://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
Development Modehttp://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
Development Mode helps in debugging your project: when you change your Java-code
just press Ctrl+F5 in your browser — and your changes will come into force! Mrrwah!
Code Splitting
when the neccessary code was loaded
Code Splitting Spell isoid createAsync(final MClient client) {
GWT.runAsync(...) public void onFailure(Throwable err) { . . . }
public void onSuccess() { if (instance == null) { . . . instance = new Module(); . . . client.onSuccess(instance); } });} when the neccessary code was loaded
Code Splitting Spell isSp createAsync(final MClient client) {
GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable err) { . . . }
public void onSuccess() { if (instance == null) { . . . instance = new Module(); . . . client.onSuccess(instance); } });} when the neccessary code was loaded
Code Splittingpublic static void createAsync(final MClient client) {
GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable err) { client.onUnavailable(); }
public void onSuccess() { if (instance == null) { instance = new Module(); } client.onSuccess(instance); } });} when the neccessary code was loaded
Code Splitting lets you specify separate parts of your project to load:
so you can split your project in large modules — and your users will feel themselves happy! Brrlyawrr!
<Module>.gwt.xml
<Module>.gwt.xml⁕ Components
a list of code components you use
<Module>.gwt.xml⁕ Components
a list of code components you use
⁕ Browsers
a list of browsers to be a target of project compilation
<Module>.gwt.xml⁕ Components
a list of code components you use
⁕ Browsers
a list of browsers to be a target of project compilation
⁕ Locales
a list of locales supported in your project
<Module>.gwt.xml⁕ Components
a list of code components you use
⁕ Browsers
a list of browsers to be a target of project compilation
⁕ Locales
a list of locales supported in your project
⁕ Debug
turning debug information output on/off
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User"/>
⁕ Browsers
⁕ Locales
⁕ Debug
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User"/>
⁕ Browsers
<set-property name="user.agent" value="ie6,gecko1_8,safari" />
⁕ Locales
⁕ Debug
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User"/>
⁕ Browsers
<set-property name="user.agent" value="ie6,gecko1_8,safari" />
⁕ Locales
<extend-property name="locale" values="fr_CA,de" /> <set-property-fallback name="locale" value="fr_CA" />
⁕ Debug
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User"/>
⁕ Browsers
<set-property name="user.agent" value="ie6,gecko1_8,safari" />
⁕ Locales
<extend-property name="locale" values="fr_CA,de" /> <set-property-fallback name="locale" value="fr_CA" />
⁕ Debug [<Module>Debug.gwt.xml]
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User"/>
⁕ Browsers
<set-property name="user.agent" value="ie6,gecko1_8,safari" />
⁕ Locales
<extend-property name="locale" values="fr_CA,de" /> <set-property-fallback name="locale" value="fr_CA" />
⁕ Debug [<Module>Debug.gwt.xml]
<inherits name="com.example.MainModule" /> <set-property name="log_level" value="DEBUG" />
<Module>.gwt.xml⁕ Components
<inherits name="com.google.gwt.user.User" />
⁕ Browsers
<set-property name="user.agent" value="ie6,gecko1_8,safari" />
⁕ Locales
<extend-property name="locale" values="fr_CA,de" /> <set-property-fallback name="locale" value="fr_CA" />
⁕ Debug [<Module>Debug.gwt.xml]
<inherits name="com.example.MainModule" /> <set-property name="log_level" value="DEBUG" />
.gwt.xml file — is almost the same thing as web.xml file for web application:
here lies all of the project configuration
MVP
R
ModelViewController
ModelViewController
API/DB
ModelViewController
calls
ModelViewPresenter
ModelViewPresenter
events
upda
te
ModelViewPresenter
Reverse
ModelViewPresenter
Reverse
PresenterEvent Bus
PresenterEvent Bus
PresenterPresenter
PEB
PP P P P P
Hmmm... To be honest, seems I did not understand the difference
between all those mah-fah-am-wee-pee...
geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
Difference between MVC and MVP
geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
The article about MVC/MVP difference
tv.jetbrains.net/videocontent/gwt-event-bus-basics
The video with the example of EventBus in work
tv.jetbrains.net/videocontent/gwt-event-bus-basics
The video with the example of EventBus in work
EventBus is the central communication channel
Deferred Binding
Deferred Binding
In response to the lack of Reflection
Deferred Binding
Dynamic implementation of any interface (and just in case if it is actually required)
In response to the lack of Reflection
Deferred Binding
GWT.create(....class) spell
Dynamic implementation of any interface (and just in case if it is actually required)
In response to the lack of Reflection
Deferred Binding
GWT.create(....class) spell
Dynamic implementation of any interface (and just in case if it is actually required)
CompileTime-binding
In response to the lack of Reflection
Deferred Binding
GWT.create(....class) spell
Dynamic implementation of any interface (and just in case if is actually required)
CompileTime-binding
In response to the lack of Reflection
Deferred Binding
PopupImpl: public void setVisible(boolean visible) { // ... common code for all implementations of PopupPanel ...
// If the PopupImpl creates an iframe shim, it's also // necessary to hide it as well. impl.setVisible(getElement(), visible);}
Deferred Binding
PopupImpl: public void setVisible(boolean visible) { // ... common code for all implementations of PopupPanel ...
// If the PopupImpl creates an iframe shim, it's also // necessary to hide it as well. impl.setVisible(getElement(), visible);}
PopupImplIE6: public native void setVisible(Element popup, boolean visible) /*-{ if (popup.__frame) { popup.__frame.style.visibility = visible ? 'visible' : 'hidden'; } }-*/;
Deferred Binding
<replace-with class="com.google.gwt...PopupImplIE6"> <when-type-is class="com.google.gwt...PopupImpl" /> <any> <when-property-is name="user.agent" value="ie6" /> <when-property-is name="user.agent" value="ie6_1" /> </any></replace-with>
Deferred Binding
<replace-with class="com.google.gwt...PopupImplIE6"> <when-type-is class="com.google.gwt...PopupImpl" /> <any> <when-property-is name="user.agent" value="ie6" /> <when-property-is name="user.agent" value="ie6_1" /> </any></replace-with>
private static final PopupImpl impl = GWT.create(PopupImpl.class);
www.docstoc.com/docs/53396874/Deferred-Binding-The-Magic-of-GWT
Slides on Deferred Binding
www.docstoc.com/docs/53396874/Deferred-Binding-The-Magic-of-GWT
Slides on Deferred Binding
Deferred Binding is a tool to create cross-browserand translingual implementations.
Namely for techniques that will differ betweencontexts of project usage.
★ Attention ★Attention!
Dependency Injection
Dependency Injection
Using GWT INjection / Guice frameworks
Dependency Injection
Binding instances to interfaces at one point (thus we achieve separation of behaviorfrom implementing solution)
Using GWT INjection / Guice frameworks
Dependency Injection
Lets you forget about XML-settings andfactories
Binding instances to interfaces at one point(thus we achieve separation of behaviorfrom implementing solution)
Using GWT INjection / Guice frameworks
Dependency Injection
Lets you forget about XML-settings andfactories
Binding instances to interfaces at one point(thus we achieve separation of behaviorfrom implementing solution)
Runtime-binding
Using GWT INjection / Guice frameworks
Dependency Injection
Lets you forget about XML-settings andfactories
Binding instances to interfaces at one point (thus we achieve separation of behaviorfrom implementing solution)
Runtime-binding
Using GWT INjection / Guice frameworks
Dependency Injectionclass MyModule extends AbstractGinModule { @Override protected void configure() { bind(Something.class).toProvider(SomethingProvider.class); bind(Any.class).in(Singleton.class); bind(Foo.class).to(SomeFooImpl.class); }}
class Bar { @Inject private Any any; private final Something something; private final Foo foo;
@Inject public Bar(Something something, Foo foo) { }}
Dependency Injection
@GinModules(MyModule.class)interface class MyGinjector extends Ginjector { public Something getSomething(); public Foo getFoo();
}
code.google.com/p/google-guice/wiki/Motivation?tm=6
Guice wiki-pages
Dependency Injection lets you easily manage your logical implementations.
Even when your project is running.
For example, you can switch database drivers or
service implementations.
These are the things you've doneusing application-context.xml
in Spring, but much better.
Annotations are wonderrrful!
Remote Service
Remote Service
public interface StringReverserService extends RemoteService { public String reverseString(String stringToReverse);}
Remote Service
public interface StringReverserService extends RemoteService { public String reverseString(String stringToReverse);}
public interface StringReverserServiceAsync { void reverseString(String stringToReverse, AsyncCallback async);}
Remote Service
public interface StringReverserService extends RemoteService { public String reverseString(String stringToReverse);}
public interface StringReverserServiceAsync { void reverseString(String stringToReverse, AsyncCallback async);}
public class StringReverserServiceImpl extends RemoteServiceServlet implements StringReverserService { . . . }
Remote Service
public interface StringReverserService extends RemoteService { public String reverseString(String stringToReverse);}
public interface StringReverserServiceAsync { void reverseString(String stringToReverse, AsyncCallback async);}
public class StringReverserServiceImpl extends RemoteServiceServlet implements StringReverserService { . . . }
StringReverserServiceAsync reverserService = (StringReverserServiceAsync) GWT.create(StringReverserService.class);
developerlife.com/tutorials/?p=125
Tutorial on creating Remote Services
Remote Services is server-side APIbuilt using Java interfaces
JUnit
JUnitpublic class StockWatcherTest extends GWTTestCase {
public String getModuleName() { return "com.google.gwt.sample.stockwatcher.StockWatcher"; } . . .
}
JUnitpublic class StockWatcherTest extends GWTTestCase {
. . .
FooPresenter.IFooView fooView = Mockito.mock(FooPresenter.IFooView.class); ... = new FooPresenter(..., fooView); Mockito.verify(fooView).someViewMethod(...);}
GWT-code is easy to testbecause of JUnit support
Deficiencies and observations
Anyway, you need good skills in JavaScript
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibility
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portals
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concern
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole application
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little information
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little informationAlthough, they are for sure much easier to understand when debug info is on
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
Development Mode works slower than real code: so you may encounter problems in events succession
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little informationAlthough, they are for sure much easier to understand when debug info is on
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
Development Mode works slower than real code: so you may encounter problems in events successionНey, you say you've never debugged code using alerts?!
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little informationAlthough, they are for sure much easier to understand when debug info is on
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
Development Mode works slower than real code: so you may encounter problems in events successionНey, you say you've never debugged code using alerts?!
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little informationAlthough, they are for sure much easier to understand when debug info is on
Regu
lar
expr
essi
ons
a re
just
a w
rapp
e r f
or Ja
vaSc
r ipt
RegExp
Deficiencies and observations
Anyway, you need good skills in JavaScript
One JS-error crashes the whole application
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibility
Development Mode works slower than real code: so you may encounter problems in events succession
No guidance at non-Java Server-Side (Python & GAE, for example)
JavaScript-errors provide little information
Regu
lar
expr
essi
ons
a re
just
a w
rapp
e r f
or Ja
vaSc
r ipt
RegExp
Deficiencies and observations
Anyway, you need good skills in JavaScriptEspecially when using external JS-libraries
One JS-error crashes the whole applicationHowever, we have GWT.setUncaughtExceptionHandler
«Manual» makeup (HTMLPanel) becomes not sustainable and has no cross-browser compatibilityGWT is for web-applications, but not pretentious portalsYou can write your own (or extend existing) components, but then thinking about cross-browser compatibility is your concernAnd you'll need to make a detailed lecture about GWT components system for your makeup men, by the way
Development Mode works slower than real code: so you may encounter problems in events successionНey, you say you've never debugged code using alerts?!
No guidance at non-Java Server-Side (Python & GAE, for example)Well, let's buid it on top of RequestBuilder
JavaScript-errors provide little informationAlthough, they are for sure much easier to understand when debug info is on
Regu
lar
expr
essi
ons
a re
just
a w
rapp
e r f
or Ja
vaSc
r ipt
RegExp
www.linux.org.ru/forum/talks/4497412
Here GWT drawbacks are discussed
Every reasoned weakness in GWT have a rational solution.
galak-sandbox.blogspot.com/2010/10/gwt.html
Summary on GWT-code optimization
And I would ask for a beer now!
3. mvp4g
code.google.com/p/mvp4g/
mvp4g framework web-page
● What helps?● Annotation system● RMVP realization● EventBus realization● URL, HistoryConverters, #!● Multimodularity● PlaceService● Remarks
mvp4g framework helps to
work with (R)MVP
mvp4g framework helps to
work with (R)MVP
organize multi-modular applications
mvp4g framework helps to
work with (R)MVP
organize multi-modular applications
design and develop events buses
mvp4g framework helps to
work with (R)MVP
organize multi-modular applications
design and develop events buses
work with history (incl. hashbangs #!)
mvp4g framework helps to
work with (R)MVP
organize multi-modular applications
design and develop events buses
work with history (incl. hashbangs #!)
... also constantly being improved
mvp4g framework helps to
work with (R)MVP
organize multi-modular applications
design and develop events buses
work with history (incl. hashbangs #!)
... also constantly being improved
mvp4g framework helps to
mvp4gshowcase.appspot.com
mvp4g framework showcase
mvp4gshowcase.appspot.com
mvp4g framework showcase
code.google.com/p/mvp4g/wiki/Mvp4g_vs_GWTP
Comparison of code written with mvp4g or native GWT
code.google.com/p/mvp4g/wiki/Mvp4g_vs_GWTP
Comparison of code written with mvp4g or native GWT
Pierre-Laurent [email protected]
Pierre-Laurent [email protected](meet him at Google I/O '11)
The code you write using mvp4gframework is much simpler thanGWT-code without its usage.
It is achieved by Pierre correct use of annotations.
Code is written by human. It is better to help him sometimes.
Code is written by human. It is better to help him sometimes.
Annotations
Annotations
@Debug@Event@EventHandler@Events@Filters@Forward@History@InitHistory@InjectService@NotFoundHistory@PlaceService@Presenter@Service@Start
Annotations
There is Annotation Processor Factory(annotation validator that applies
just when you edit the source code)
Annotations — the power of mvp4g!Annotations — the power of mvp4g!
RMVP
RMVPannotations
@Debug@Event@EventHandler@Events@Filters@Forward@History@InitHistory@InjectService@NotFoundHistory@PlaceService@Presenter@Service@Start
RMVPpresenter
@Presenter(view=OneView.class)public class OnePresenter extends BasePresenter<IOneView, OneEventBus> { @Inject private ServiceAsync service; }
RMVPview
@Presenter(view=OneView.class)public class OnePresenter extends BasePresenter<IOneView, OneEventBus> { @Inject private ServiceAsync service; }
class OneView extends Composite implements IOneView { . . .}
RMVPreverse
@Presenter(view=OneView.class)public class OnePresenter extends BasePresenter<IOneView, OneEventBus> { @Inject private ServiceAsync service; }
class OneView extends Composite implements IOneView, ReverseViewInterface<OnePresenter> { . . .}
EventBus
EventBusannotations
@Debug@Event@EventHandler@Events@Filters@Forward@History@InitHistory@InjectService@NotFoundHistory@PlaceService@Presenter@Service@Start
EventBusevents
@Events(startView = StartView.class)public interface OneEventBus extends EventBus {
@Event public void fooEvent(...);
@Event public void barEvent(...);
}
EventBushandlers
@Events(startView = StartView.class)public interface OneEventBus extends EventBus {
@Event(handlers={FooPresenter.class, AcmePresenter.class}) public void fooEvent(...);
@Event(handlers=BarPresenter.class) public void barEvent(...);
}
FooPresenter::onFooEvent(...) {...}FooPresenter::onFooEvent(...) {...}BarPresenter::onBarEvent(...) {...}
EventBusactivation
@Events(startView = StartView.class)public interface OneEventBus extends EventBus {
@Event(handlers={FooPresenter.class, AcmePresenter.class}, activate={FooPresenter.class, AcmePresener.class}, deactivate={BarPresenter.class}) public void fooEvent(...);
@Event(handlers=BarPresenter.class, activate={BarPresenter.class}, deactivate={FooPresenter.class, AcmePresener.class}) public void barEvent(...);
}
EventBusbroadcast
@Events(startView = StartView.class)public interface OneEventBus extends EventBus {
@Event(broadcastTo=IBroadcast.class, calledMethod="boo") public void broadcastEvent(...);
}
public class Foo implements IBroadcast { public void boo(...) {...};}
EventBusfilters and stuff
@Filters(filterClasses={FilterOne.class, FilterTwo.class})@Debug(logger=CustomLogger.class)@Events(startView = StartView.class)public interface OneEventBus extends EventBus {
. . .
}
class FilterOne implements EventFilter<OneEventBus> {
@Override public boolean filterEvent(...) { return ...; }
}
History
Historyannotations
@Debug@Event@EventHandler@Events@Filters@Forward@History@InitHistory@InjectService@NotFoundHistory@PlaceService@Presenter@Service@Start
Historyhandling
@Events(..., historyOnStart = true)public interface OneEventBus extends EventBus {
@Start @InitHistory public void start();
@Event(handlers=..., navigationEvent=true, historyName="foo", historyConverter=OneHC.class) public void fooEvent(...);
@NotFoundHistory public void show404();
}
@Events(..., historyOnStart = true)public interface OneEventBus extends EventBus {
@Start @InitHistory public void start();
@Event(handlers=..., navigationEvent=true, historyName="foo", historyConverter=OneHC.class) public void fooEvent(...);
@NotFoundHistory public void show404();
}
/start
/foo
Historypassing
@Events(...)public interface OneEventBus ... {
@Event(..., navigationEvent=true, historyName="foo", historyConverter=OneHC.class) public void fooEvent(int id, Filter filter);
}
@History public class OneHC implements HistoryConverter<OneEventBus> {
@Inject private TokenGenerator tokens;
public void convertFromToken(...) { if ("foo".equals(event))
eventBus.fooEvent( Integer.parseInt(params[0]), Filter.parse(params[1])); } public String fooEvent(...) { return tokens.fooEvent(id, filter); }
}
/foo?26;all
Historyparameters
Historyhashbang
@Events(...)public interface OneEventBus ... {
@Event(...) public void fooEvent(int id, Filter filter);
}
@History public class OneHC implements HistoryConverter<OneEventBus> {
...
public boolean isCrawable() { return true; }
}
/#!foo?26;all
History and event buses are the skeletonof your site navigation system
History and event buses are the skeletonof your site navigation system
Multi-modularity
Multi-modularityannotations
@AfterLoadChildModule@BeforeLoadChildModule@ChildModule@ChildModules@DisplayChildModuleView@HistoryName@LoadChildModuleErrors
Multi-modularityURLs
company/listcompany/addcompany/edit?123user/listuser/adduser/edit?39
object/action[?parameters]
REST
Multi-modularitymodules
public interface CompanyModule extends Mvp4gModule {}
@Events(..., module=CompanyModule.class)public interface CompanyEventBus extends EventBus {...}
public interface UserModule extends Mvp4gModule {}
@Events(..., module=UserModule.class)public interface UserEventBus extends EventBus {...}
@Events(..., module=UserModule.class)public interface UserEventBus extends EventBus {
@Event(..., handlers=UserListPresenter.class, historyName="list") public void usersList(); . . .}
@Events(...)@ChildModules( @ChildModule(moduleClass=UserModule.class) @ChildModule(moduleClass=CompanyModule.class))public interface ParentEventBus extends EventBus{
@Event(modulesToLoad=UserModule.class) public void usersList();
@Event(modulesToLoad=CompanyModule.class) public void companiesList();
}Multi-modularityevent buses
@Events(...)@ChildModules( @ChildModule(moduleClass= UserModule.class, runAsync=true) @ChildModule(moduleClass= CompanyModule.class, runAsync=true))public interface ParentEventBus extends EventBus {
. . .
}
Multi-modularityasynchronousloading
History, event buses and modules are the skeletonof your site navigation system
History, event buses and modules are the skeletonof your site navigation system
You can load modules asynchronously!So user will not get the kilobyteshe needs not, if he will not visitspecified sections of your site.
You can load modules asynchronously!So user will not get the kilobyteshe needs not, if he will not visitspecified sections of your site.
PlaceService
PlaceServiceannotations
@Debug@Event@EventHandler@Events@Filters@Forward@History@InitHistory@InjectService@NotFoundHistory@PlaceService@Presenter@Service@Start
PlaceServiceoverriding
@PlaceService(CustomPlaceService.class)@Events(...)public interface MainEventBus extends EventBusWithLookup { . . .}
public class CustomPlaceService extends PlaceService { protected void convertToken(String token) { ... } protected String[] parseToken(String token) { ... } public String tokenize(String eventName, String param) { ... } . . .}
Remarks
Remarks
There is no layouting-system yet
Remarks
There is no layouting-system yet
Multimodularity is aimedat object → action principle
Remarks
There is no layouting-system yet
Multimodularity is aimedat object → action principle
GIN/Guice are supported
Remarks
There is no layouting-system yet
Multimodularity is aimedat object → action principle
When using custom GwtEvents, you should keep an eye on presenters activation. Callbacks — easier.
GIN/Guice are supported
Remarks
There is no layouting-system yet
Multimodularity is aimedat object → action principle
When using custom GwtEvents, you should keep an eye on presenters activation. Callbacks — easier.
There are LazyView & LazyPresenter
GIN/Guice are supported
mvp4g — is what Zoidberg has prescribed!
4. UI components
ButtonPushButtonRadioButtonCheckBoxDatePickerToggleButtonTextBoxPasswordTextBoxTextAreaHyperlink / AnchorListBoxCellListMenuBarTree, CellTreeSuggestBoxRichTextAreaFlexTable, Grid, CellTableCellBrowserTabBarDialogBox
PopupPanelStackPanel, StackLayoutPanelHorizontalPanelVerticalPanelFlowPanelVerticalSplitPanelHorizontalSplitPanelSplitLayoutPanelDockPanel, DockLayoutPanelTabPanel, TabLayoutPanelDisclosurePanel
code.google.com/webtoolkit/doc/latest/RefWidgetGallery.html
GWT components library
UiBinder: .ui.xml<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"><g:VerticalPanel styleName="my-css-style">
<g:HorizontalPanel><g:Label>Name</g:Label><g:TextBox ui:field="nameBox">Babylen</g:TextBox>
</g:HorizontalPanel>
<g:HorizontalPanel><g:Label>Family name</g:Label><g:TextBox ui:field="fnameBox">Tatarsky</g:TextBox>
</g:HorizontalPanel>
<g:ListBox ui:field="namesLst" visibleItemCount="1" />
<g:Button ui:field="submit">Submit</g:Button></g:VerticalPanel>
</ui:UiBinder>
UiBinder: .javapublic class SettingsForm extends Composite { interface SFormBinder extends UiBinder<Widget, SettingsForm> {} private static FormBinder uiBinder = GWT.create(SFormBinder.class);
@UiField TextBox nameBox; @UiField TextBox fnameBox; @UiField ListBox namesLst;
public HelloWorld(String... names) { initWidget(uiBinder.createAndBindUi(this)); for (String name : names) { namesLst.addItem(name); } }
@UiHandler("submit") public onSubmit(ClickEvent e) { ... }
}
HTMLPanel
<g:HTMLPanel><div>Some div</div>
<div> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> </div> <p> <span>Some span</span> </p></g:HTMLPanel>
Manual make-up vs. Cross-browser support
.gwt-Button { font-size: 150%; }
.b-popup { position: absolute; }
Attention!
In GWT, there is an entire vast library of components
and an armory of layouts.
Though, the fact of developing a site withits own unique style is fraught with
cross-browser compatibility problems.
Custom components
Custom components
Do not inherit, but delegate
Custom components
Do not inherit, but delegate
@UiConstructorpublic MyCustomWidget(String defaultText) { . . .}
public void setMaxLength(int maxLength) { ... }
Custom components
Do not inherit, but delegate
@UiConstructorpublic MyCustomWidget(String defaultText) { initWidget(uiBinder.createAndBindUi(this));}
public void setMaxLength(int maxLength) { ... }
Customization vs. Cross-browser support
Attention!
So it is better either to keep your site design as simple as possible...
...either to allocatea significant amount of time
for UI designers and programmers to work through your own
component library.
...either to allocatea significant amount of time
for UI designers and programmers to work through your own
component library.
The lack of UI Designerwas a drawback
for someone
The lack of UI Designerwas a drawback
for someone
5. Layouting
Reasons?
Reasons?If your site is built with logical blockswhich are visually placed in different order,depending on context.
Reasons?If your site is built with logical blockswhich are visually placed in different order,depending on context.
For example, portlets or custom widgets
Reasons?If your site is built with logical blockswhich are visually placed in different order,depending on context.
For example, portlets or custom widgets
Means, when it is a flexible site.
Reasons?If your site is built with logical blockswhich are visually placed in different order,depending on context.
For example, portlets or custom widgets
Means, when it is a flexible site.
These mechanics are not yet implemented in mvp4g
Layouts
LIST ITEM EDIT
B
B
B
CA
A
C
A
C
D
public class LayoutList implements Layout { }
LayoutList.ui.xml:<FlowPanel ui:field="a"/><FlowPanel ui:field="b"/><FlowPanel ui:field="c"/>
Layouts
LIST ITEM EDIT
public enum Place { A, B, C, D};
public interface Layout { public LayoutId id(); public HasWidgets place(Place place); Map<Place, HasWidgets> places();}
public class LayoutItem implements Layout { }
LayoutItem.ui.xml:<FlowPanel ui:field="a"/><FlowPanel ui:field="b"/><FlowPanel ui:field="c"/>
public class LayoutEdit implements Layout { }
LayoutEdit.ui.xml:<FlowPanel ui:field="a"/><FlowPanel ui:field="b"/><FlowPanel ui:field="c"/><FlowPanel ui:field="d"/>
Layouts
LIST ITEM EDIT
B
B
B
CA
A
C
A
C
D
Base page
BasePage.ui.xml:<FlowPanel ui:field="toolbar"/><FlowPanel ui:field="layout"/><FlowPanel ui:field="footer"/><FlowPanel ui:field="copy"/>
toolbar
layout
footercopy
public enum Portal implements MakesLink {
NEWS_LIST(LayoutId.LIST, <event-spec>, <options>), NEWS_EDIT(LayoutId.EDIT, <event-spec>, <options>), NEWS_VIEW(LayoutId.ITEM, <event-spec>, <options>), NEWS_DELETE(LayoutId.ITEM, <event-spec>, <options>) USER_LIST(LayoutId.LIST, <event-spec>, <options>), USER_EDIT(LayoutId.EDIT, <event-spec>, <options>), USER_VIEW(LayoutId.ITEM, <event-spec>, <options>), USER_DELETE(LayoutId.ITEM, <event-spec>, <options>), . . . @Override public String makeLink() { . . . }
}
Page / Portal
public enum Portal implements MakesLink {
. . .
public class PortalUrl implements MakesLink {
PortalUrl(Portal portal[, <params>]) { ... }
public PortalUrl addParam(...) { ... } public PortalUrl fromEvent(String module, String event, String params) { ... } @Override public String makeLink() { ... }
}
}
Link
public enum Portal implements MakesLink {
. . .
public class PortalUrl implements MakesLink {
PortalUrl(Portal portal[, <params>]) { ... }
public PortalUrl addParam(...) { ... } public PortalUrl fromEvent(String module, String event, String params) { ... } @Override public String makeLink() { ... }
}
}
Link
History.newItem(Portal.USER_LIST.makeLink());History.newItem(new PortalUrl(Portal.USER_EDIT, uid).makeLink());History.newItem(userTokenGenerator.edit(uid));
public abstract class LayoutBuilder<E extends ChildEventBus> {
public CanBuildLayout prepareFor(final Portal page) { return new CanBuildLayout { public Layout build(State state) { layout(page, state, LayoutFactory.get(page.layout).places()); } } } public abstract void layout(Portal page, State state, Map<Place, HasWidgets> places);
}
Layout builder
public class UserHistoryConverter implements HistoryConverter<UserEventBus> { public void convertFromToken(String evt, String param) { // можно использовать tokenGenerator PortalUrl curUrl = PortalUrl.fromToken("user",evt,param); Portal portal = curUrl.portal; eventBus.newPage(portal, layoutBuilder.prepareFor(portal)); eventBus.dispatch(curUrl);
}
}
Switching layouts
public class UserLayoutBuilder implements LayoutBuilder<UserEventBus> { public void layout(Portal page, State state, Map<Place, HasWidgets> places) switch (page) { case USER_ITEM: { eventBus.projectItem(places.get(Place.A)); eventBus.projectCalendar(places.get(Place.B)); eventBus.projectPreview(places.get(Place.C)); } break; case USER_LIST: switch (state) { ... } break; case USER_EDIT: ... break; }
}
}
Builder implementation
public interface ChildEventBus { @Event(forwardToParent=true) public void newPage(Portal page, CanBuildLayout builder);
@Event(forwardToParent=true) public void project(Widget what, HasWidgets where);
@Event(forwardToParent=true) public void updateState(State state) }
Event buses
[ forwarded to BaseEventBus ]
public interface UserEventBus extends ChildEventBus { // navigation @Event(navigationEvent=true, ...) public void list(); @Event(navigationEvent=true, handlers=UserShowPresenter.class, historyConverter=UserHistoryConverter.class) public void show(String uid); @Event(navigationEvent=true, ...) public void edit(String uid); . . .
// projection @Event(handlers=UserShowPresenter.class,calledMethod="prjItem") public void projectItem(HasWidgets where); @Event(handlers=UserShowPresenter.class,calledMethod="prjPrvw") public void projectPreview(HasWidgets where); @Event(handlers=CalendarPresenter.class,calledMethod="project") public void projectCalendar(HasWidgets where)
}
Event buses
Layouting helps us to build and to live!
github.com/shamansir/gwt-mvp4g-layouting-demo
I've wanted to make a demo for you, but had no time ;(
github.com/shamansir/gwt-mvp4g-layouting-demo
I've wanted to make a demo for you, but had no time ;(
Follow
github.com/shamansir/gwt-mvp4g-layouting-demo
I've wanted to make a demo for you, but had no time ;(
Фото gwt-mvp4g-layouting-demo.appspot.com
QRCode gwt-mvp4g-layouting-demo.appspot.com
6. Non-Java API
code.google.com/p/google-web-toolkit-doc-1-5/wiki/GettingStartedJSON
Accidentally, here is the main point
Possibility — exists
RequestBuilder
General context
Call chains
Insering JS-object into HTML-markup
(you can parse them using JSNI)
Advantage : Independence from serialization
Disadvantage : Unconventional approach with all the consequences
shamansir-ru.tumblr.com/post/1728720550/deferred-api-gwt-rpc
And here is the source code
7. i18n
Messages / Constants
public interface LoginMessages extends Messages { public String enterName(); public String emailExists(String email); public String emailInvalid(String email); public String loginFailed(String username); public String youFailedNTimes(@PluralCount int times);}
public interface MenuConstants extends Constants { public String login(); public String logout(); public String contacts(); public String settings();}
LoginMessages_ru.propertiesenterName = Введите имяemailExists = E-mail {0} зарегистрирован в системеemailInvalid = Некорректный e-mail {0}loginFailed = Не удалось зайти пользователем {0}youFailedNTimes = Кол-во неудачных попыток: {0,number}youFailedNTimes[one] = {0,number} неудачная попыткаyouFailedNTimes[few] = {0,number} неудачных попыток
MenuConstants_ru.propertieslogin= Войтиlogout = Выйтиcontacts = Контактыsettings = Настройки
Messages / Constants
<e:MyTextBox ui:field="box" defaultText="{messages.enterText}" />
LoginMessages messages = GWT.create(LoginMessages.class);MenuConstants constants = GWT.create(MenuConstants.class);
<ui:with type="....LoginMessages" field="messages" />
Messages / Constants
Messages / Constants
<txt:msg key="messageKey">Message</txt:msg>
<ui:UiBinder ... xmlns:txt="ui:with:...MyMessages" />
ErrorsConstants_ru.propertiesERR_101 = Ошибка авторизацииERR_102 = Неизвестная ошибкаERR_103 = Ресурс не найден
errors = ERR_101, ERR_102, ERR_103
public interface ErrorsConstants extends ConstantsWithLookup { Map<String, String> errors(); }
Messages / Constants
Mae'n hawdd iawn i gyfieithu prosiectau GWT...
Mae'n hawdd iawn i gyfieithu prosiectau GWT...
...if you'll teach yourtranslators to manage.properties-files or givethem PoEdit or Pootle
...if you'll teach yourtranslators to manage.properties-files or givethem PoEdit or Pootle
ResourceBundles
Позволяют использовать локализованные ресурсы
8. Conclusion
experika.com
We applied those techniques for Experika
experika.com
Welcome to Experika
Thanks toVitaly Gashock, for intoducing mvp4g to me and partnership
Mikhail Kashkin, for mentoring
http://www.vurt.ru
http://twitter.com/vgashock
Alexey Kakunin, for 道 and 先生
http://www.emdev.ru
profiles.google.com/shaman.sir
Anthony Kotenko
Thank you.
Made with LibreOffice 3.3.1 Impress
More questions?
GWT FTW!
Philip J. Fry, Dr. Zoidber, Nibbler and Hypnotoad are all the Futurama series characters
and all created by Matt Groening
Homer Simpson is the characterof The Simpsons series
and also created by Matt Groening
All the phrases they tellin this presentation have no relation
nor for these characters, nor for series noticed above,
nor for their creator