Professional Documents
Culture Documents
GWT + mvp4g
goo.gl/4GgnS
[ PDF, ~2MB ]
Anthony Kotenko
6 years in Java EE development
6 years in UI development
Sex: male
Anthony Kotenko
6 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
7. i18n in GWT
Like so:
I have a question!
Discussion is important
1. Briefly 'bout GWT
Google
GWT Web
Toolkit
/ˈɡwɪt/
code.google.com/webtoolkit/
GWT
/ˈɡwɪt/
/ˈɡwɪt/
Used in these projects:
Google Wave wave.google.com
Whirled whirled.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
The most complete
A box of useful tools
for web-developer
widgets
http://old.ongwt.com/public/WindowsLiveWriter_GWTMosaicnicewidgetlibrary_D777_image_2.png
widgets
optimization
http://radar.oreilly.com/200912081729.jpg
widgets
optimization
cross-browserly!
http://2.bp.blogspot.com/_VzXmgKXrn6Y/TKB2a3eZTBI/AAAAAAAAAaQ/mn7NmabwO8U/s1600/browsers%5Btsksoft.blogspot.com%5D.jpg
widgets
optimization
cross-browserly!
on-the-fly development
http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug
http://www.ibm.com/developerworks/library/j-ajax4/eclipse-debug.jpg
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug
RPC
API/DB
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug
http://test.ical.ly/wp-content/uploads/2010/04/i18n.png
widgets
optimization
cross-browserly!
on-the-fly development
OOP benefits debug
Version 1.6
Google Eclipse Plugin
Project structure matches
Web 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
quake2-gwt-port.appspot.com
Quake 2 in browser
https://y2mzuw.blu.livefilestore.com/y1m9Pc7Xi6qtMdKU_hGCv7VZrEKiIZTYqDIC5laL9A0LxracK6AN8EAet90MRtWlBJISphNp_Y8QCxpb0p9v1y
lTCdwATCweUs9496DC14_ijBjhnpj2oRWme9B1SC2C0t9HJ1wX8RTsVQAhyDYCsqfeg/quake-html5-04-02-2010.jpg
quake2-gwt-port.appspot.com
Quake 2 in browser
Trendy
Youth
Modern
/ˈɡwɪt/
GWT is actively developing project,
however it already contains
everything you need
2. GWT Conceptions
almaer.com/blog/rotating-java-and-javascript-on-the-server
EntryPoint
Any GWT-project starts from entry point
● Java → JavaScript, JSNI
● Development Mode
● Code Splitting
● <Module>.gwt.xml
● Deferred Binding
● Dependency Injection
● Remote Service
● JUnit
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;
window[callback] = function(jsonObj) {
handler.@com.google.gwt.sample.stockwatcher
.client.StockWatcher::handleJsonResponse(
Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
window[callback + "done"] = true;
}
setTimeout(function() {
if (!window[callback + "done"]) {
handler.@com.google.gwt.sample.stockwatcher
.client.StockWatcher::handleJsonResponse(
Lcom/google/gwt/core/client/JavaScriptObject;)(null);
}
document.body.removeChild(script);
delete window[callback];
JSNI
delete window[callback + "done"];
}, 1000);
document.body.appendChild(script);
}-*/;
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 JSNI
Google Maps widget,
integrated with the help of JSNI
Flash-object
using JavaScript-callbacks,
which are called through JSNI JSNI
You can use JSNI to
make third-party components
written in JavaScript
become GWT-widgets
Development Mode
♥
Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
♥
Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
♥
Development Mode
http://lc:8080/ui/?gwt.codesvr=lc:9997#!job/start
http://lh4.ggpht.com/_a0imbbK4r5U/Sxwd1oVpXiI/AAAAAAAAAjc/gsRkQUn9kZI/gwt-dev-mode.png
Development Mode
http://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
GWT.runAsync(...)
public void onFailure(Throwable err) {
. . .
}
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable err) {
. . .
}
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable err) {
client.onUnavailable();
}
⁕ Browsers
⁕ Browsers
⁕ Locales
⁕ Browsers
⁕ Locales
⁕ Debug
<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
⁕ 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>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
⁕ Debug [<Module>Debug.gwt.xml]
⁕ Browsers
<set-property name="user.agent"
value="ie6,gecko1_8,safari" />
⁕ Locales
⁕ Debug [<Module>Debug.gwt.xml]
View
Controller
Model
View
calls
Controller
Model
View
Presenter
Model
View
events
update
Presenter
Reverse
Model
View
Presenter
Reverse
Model
View
Presenter
Presenter
Event Bus
er Presenter Pre
Event Bus
P P P P P P P
EB
Hmmm... To be honest, seems
I did not understand the difference
between all those mah-fah-am-wee-pee...
Difference between MVC and MVP
geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
The article about MVC/MVP difference
geekswithblogs.net/kobush/archive/2006/01/09/65305.aspx
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
tv.jetbrains.net/videocontent/gwt-event-bus-basics
EventBus is the central communication channel
Deferred Binding
Deferred Binding
In response to the lack of Reflection
Deferred Binding
In response to the lack of Reflection
GWT.create(....class) spell
Deferred Binding
In response to the lack of Reflection
GWT.create(....class) spell
CompileTime-binding
Deferred Binding
In response to the lack of Reflection
GWT.create(....class) spell
CompileTime-binding
Deferred Binding
PopupImpl: public void setVisible(boolean visible) {
// ... common code for all implementations of PopupPanel ...
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
Deferred Binding is a tool to create cross-browser
and translingual implementations.
Namely for techniques that will differ between
contexts of project usage.
Attention!
★ Attention ★
Dependency Injection
Dependency Injection
Using GWT INjection / Guice frameworks
Dependency Injection
Using GWT INjection / Guice frameworks
Runtime-binding
Dependency Injection
Using GWT INjection / Guice frameworks
Runtime-binding
Dependency Injection
class 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 {
}
Guice wiki-pages
code.google.com/p/google-guice/wiki/Motivation?tm=6
Dependency Injection lets you easily manage
your logical implementations.
Even when your project is running.
StringReverserServiceAsync reverserService =
(StringReverserServiceAsync) GWT.create(StringReverserService.class);
Tutorial on creating Remote Services
developerlife.com/tutorials/?p=125
Remote Services is server-side API
built using Java interfaces
JUnit
JUnit
public class StockWatcherTest extends GWTTestCase {
. . .
}
JUnit
public class StockWatcherTest extends GWTTestCase {
. . .
FooPresenter.IFooView fooView =
Mockito.mock(FooPresenter.IFooView.class);
... = new FooPresenter(..., fooView);
Mockito.verify(fooView).someViewMethod(...);
}
GWT-code is easy to test
because of JUnit support
Deficiencies and observations
Anyway, you need good skills in JavaScript
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries
Deficiencies and observations
Anyway, you need good skills in JavaScript
Especially when using external JS-libraries
www.linux.org.ru/forum/talks/4497412
Every reasoned weakness in GWT
have a rational solution.
Summary on GWT-code optimization
galak-sandbox.blogspot.com/2010/10/gwt.html
And I would ask for a beer now!
3. mvp4g
mvp4g framework web-page
code.google.com/p/mvp4g/
● What helps?
● Annotation system
● RMVP realization
● EventBus realization
● URL, HistoryConverters, #!
● Multimodularity
● PlaceService
● Remarks
mvp4g framework helps to
mvp4g framework helps to
mvp4gshowcase.appspot.com
mvp4g framework showcase
mvp4gshowcase.appspot.com
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
code.google.com/p/mvp4g/wiki/Mvp4g_vs_GWTP
Pierre-Laurent Coirier
plcoirier@gmail.com
Pierre-Laurent Coirier
plcoirier@gmail.com
(meet him at Google I/O '11)
The code you write using mvp4g
framework is much simpler than
GWT-code without its usage.
Code
Code is
is written
written by
by human.
human.
ItIt is
is better
better to
to help
help him
him sometimes.
sometimes.
Annotations
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
Annotations @InitHistory
@InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
There is Annotation Processor Factory
(annotation validator that applies
just when you edit the source code)
Annotations
Annotations
Annotations —
— the
the power
power of
of mvp4g!
mvp4g!
RMVP
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
RMVP @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {
@Inject
private ServiceAsync service;
RMVP }
presenter
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {
@Inject
private ServiceAsync service;
RMVP }
view class OneView extends Composite
implements IOneView {
. . .
}
@Presenter(view=OneView.class)
public class OnePresenter extends
BasePresenter<IOneView, OneEventBus> {
@Inject
private ServiceAsync service;
RMVP }
reverse class OneView extends Composite
implements IOneView,
ReverseViewInterface<OnePresenter> {
. . .
}
EventBus
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
EventBus @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {
EventBus
@Event
public void fooEvent(...);
@Event
events public void barEvent(...);
}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {
@Event(handlers={FooPresenter.class,
EventBus AcmePresenter.class})
public void fooEvent(...);
handlers @Event(handlers=BarPresenter.class)
public void barEvent(...);
FooPresenter::onFooEvent(...) {...}
FooPresenter::onFooEvent(...) {...}
BarPresenter::onBarEvent(...) {...}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {
@Event(handlers={FooPresenter.class,
AcmePresenter.class},
EventBus
activate={FooPresenter.class,
AcmePresener.class},
deactivate={BarPresenter.class})
@Event(handlers=BarPresenter.class,
activate={BarPresenter.class},
deactivate={FooPresenter.class,
AcmePresener.class})
public void barEvent(...);
}
@Events(startView = StartView.class)
public interface OneEventBus
extends EventBus {
EventBus @Event(broadcastTo=IBroadcast.class,
calledMethod="boo")
broadcast public void broadcastEvent(...);
. . .
EventBus
filters and stuff
class FilterOne implements EventFilter<OneEventBus> {
@Override
public boolean filterEvent(...) { return ...; }
}
History
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
History @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@Events(..., historyOnStart = true)
public interface OneEventBus
extends EventBus {
@Start
@InitHistory
public void start();
History @Event(handlers=...,
navigationEvent=true,
historyName="foo",
handling historyConverter=OneHC.class)
public void fooEvent(...);
@NotFoundHistory
public void show404();
}
@Events(..., historyOnStart = true)
public interface OneEventBus
extends EventBus {
@Start
@InitHistory
/start public void start();
History @Event(handlers=...,
navigationEvent=true,
historyName="foo",
passing historyConverter=OneHC.class)
/foo public void fooEvent(...);
@NotFoundHistory
public void show404();
}
@Events(...)
public interface OneEventBus ... {
@Event(..., navigationEvent=true,
historyName="foo",
historyConverter=OneHC.class)
/foo?26;all public void fooEvent(int id,
Filter filter);
}
@Events(...)
public interface OneEventBus ... {
@Event(...)
/#!foo?26;all public void fooEvent(int id,
Filter filter);
hashbang ...
}
History
History and
and event
event buses
buses are
are the
the skeleton
skeleton
of
of your
your site
site navigation
navigation system
system
Multi-
modularity
@AfterLoadChildModule
@BeforeLoadChildModule
Multi- @ChildModule
@ChildModules
modularity @DisplayChildModuleView
@HistoryName
annotations @LoadChildModuleErrors
REST
object/action[?parameters]
Multi- company/list
company/add
modularity company/edit?123
user/list
URLs user/add
user/edit?39
public interface UserModule
extends Mvp4gModule {}
Multi-
modularity
modules @Events(..., module=CompanyModule.class)
public interface CompanyEventBus
extends EventBus {...}
@Events(..., module=UserModule.class)
public interface UserEventBus
extends EventBus {...}
@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)
Multi- }
public void companiesList();
modularity
event buses
@Events(..., module=UserModule.class)
public interface UserEventBus extends EventBus {
@Event(..., handlers=UserListPresenter.class,
historyName="list")
public void usersList();
. . .
}
@Events(...)
@ChildModules(
@ChildModule(moduleClass=
UserModule.class,
runAsync=true)
@ChildModule(moduleClass=
CompanyModule.class,
runAsync=true))
modularity . . .
asynchronous }
loading
History,
History, event
event buses
buses and
and modules
modules are
are the
the skeleton
skeleton
of
of your
your site
site navigation
navigation system
system
You
You can
can load
load modules
modules asynchronously!
asynchronously!
So
So user
user will
will not
not get
get the
the kilobytes
kilobytes
he
he needs
needs not,
not, ifif he
he will
will not
not visit
visit
specified
specified sections
sections of of your
your site.
site.
PlaceService
@Debug
@Event
@EventHandler
@Events
@Filters
@Forward
@History
PlaceService @InitHistory
annotations @InjectService
@NotFoundHistory
@PlaceService
@Presenter
@Service
@Start
@PlaceService(CustomPlaceService.class)
@Events(...)
public interface MainEventBus
extends EventBusWithLookup {
. . .
}
PlaceService
overriding
public class CustomPlaceService
extends PlaceService {
protected void convertToken(String token) { ... }
protected String[] parseToken(String token) { ... }
public String tokenize(String eventName,
String param) { ... }
. . .
}
Remarks
There is no layouting-system yet
Remarks
There is no layouting-system yet
Multimodularity is aimed
at object → action principle
Remarks
There is no layouting-system yet
Multimodularity is aimed
at object → action principle
Remarks
There is no layouting-system yet
Multimodularity is aimed
at object → action principle
Remarks
When using custom GwtEvents,
you should keep an eye on presenters
activation. Callbacks — easier.
There is no layouting-system yet
Multimodularity is aimed
at object → action principle
Remarks
When using custom GwtEvents,
you should keep an eye on presenters
activation. Callbacks — easier.
code.google.com/webtoolkit/doc/latest/RefWidgetGallery.html
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:Button ui:field="submit">Submit</g:Button>
</g:VerticalPanel>
</ui:UiBinder>
UiBinder: .java
public class SettingsForm extends Composite {
interface SFormBinder extends UiBinder<Widget,
SettingsForm> {}
private static FormBinder uiBinder =
GWT.create(SFormBinder.class);
@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>
Attention!
.b-popup { position: absolute; }
@UiConstructor
public MyCustomWidget(String defaultText) {
. . .
}
@UiConstructor
public MyCustomWidget(String defaultText) {
initWidget(uiBinder.createAndBindUi(this));
}
...either
...either to
to allocate
allocate
aa significant
significant amount
amount ofof time
time
for
for UI
UI designers
designers andand programmers
programmers
to
to work
work through
through your
your own
own
component
component library.
library.
The lack of UI Designer
was a drawback
for someone
The lack of UI Designer
was a drawback
for someone
5. Layouting
Reasons?
Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.
Reasons?
If your site is built with logical blocks
which are visually placed in different order,
depending on context.
B B
A
A A D
C
C B C
public interface Layout {
LayoutList.ui.xml: LayoutEdit.ui.xml:
<FlowPanel ui:field="a"/> <FlowPanel ui:field="a"/>
<FlowPanel ui:field="b"/> <FlowPanel ui:field="b"/>
<FlowPanel ui:field="c"/> <FlowPanel ui:field="c"/>
<FlowPanel ui:field="d"/>
public class LayoutItem
implements Layout { }
LayoutItem.ui.xml:
<FlowPanel ui:field="a"/>
<FlowPanel ui:field="b"/>
<FlowPanel ui:field="c"/>
B B
A
A A D
C
C B C
Base page
toolbar
layout
footer
copy
BasePage.ui.xml:
<FlowPanel ui:field="toolbar"/>
<FlowPanel ui:field="layout"/>
<FlowPanel ui:field="footer"/>
<FlowPanel ui:field="copy"/>
Page / Portal
public enum Portal implements MakesLink {
@Override
public String makeLink() { . . . }
}
Link
public enum Portal implements MakesLink {
. . .
}
Link
public enum Portal implements MakesLink {
. . .
} History.newItem(Portal.USER_LIST.makeLink());
History.newItem(new PortalUrl(Portal.USER_EDIT, uid).makeLink());
History.newItem(userTokenGenerator.edit(uid));
Layout builder
public abstract class
LayoutBuilder<E extends ChildEventBus> {
}
Switching layouts
public class UserHistoryConverter
implements HistoryConverter<UserEventBus> {
eventBus.newPage(portal,
layoutBuilder.prepareFor(portal));
eventBus.dispatch(curUrl);
}
Builder implementation
public class UserLayoutBuilder
implements LayoutBuilder<UserEventBus> {
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;
}
}
Event buses
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)
[ forwarded to BaseEventBus ]
Event buses
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)
}
Layouting helps us to build and to live!
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 ;(
github.com/shamansir/gwt-mvp4g-layouting-demo
Фото gwt-mvp4g-layouting-demo.appspot.com
QRCode gwt-mvp4g-layouting-demo.appspot.com
6. Non-Java API
Accidentally, here is the main point
code.google.com/p/google-web-toolkit-doc-1-5/wiki/GettingStartedJSON
Possibility — exists
RequestBuilder
General context
Call chains
Insering JS-object into HTML-markup
shamansir-ru.tumblr.com/post/1728720550/deferred-api-gwt-rpc
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);
}
MenuConstants_ru.properties
login= Войти
logout = Выйти
contacts = Контакты
settings = Настройки
Messages / Constants
ErrorsConstants_ru.properties
ERR_101 = Ошибка авторизации
ERR_102 = Неизвестная ошибка
ERR_103 = Ресурс не найден
...if
...if you'll
you'll teach
teach your
your
translators
translators to to manage
manage
.properties-files
.properties-files or or give
give
them
them PoEdit
PoEdit or
or Pootle
Pootle
Позволяют использовать локализованные ресурсы
ResourceBundles
8. Conclusion
We applied those techniques for Experika
experika.com
Welcome to Experika
experika.com
Thanks to
Vitaly Gashock, for intoducing mvp4g to me
and partnership
http://twitter.com/vgashock
profiles.google.com/shaman.sir
Thank you.