I am attempting to design my first, super-simple GWT app, that takes advantages of most of GWT's major API features. I think I'm about 70 - 80% of the way there, but after pouring over the GWT docs, countless articles, and posting many other GWT-related questions here on SO, I am still having a tough time understanding the last 20 - 30% needed to put this app altogether.
I have called this first test app ClickItToWinIt
, and I want it to look like this:
When the user clicks the "Win" button, a picture ("win-pic.png
") pops up in the image frame on the left-hand side. When the user clicks the "Lose" button, a picture ("lose-pic.png
") pops up in the same frame. The user can click back and forth between the buttons to make the different pictures display. This would be a single-page app, hosted locally on my machine, and would only need to contain 1 homepage, 1 module/entrypoint, etc.
Please note that the packaging/class structure proposed below is surely overkill for such a simple ClickItToWinIt
app. However, before long I want to be moving on to bigger and better things with GWT, so it's important to me that I have an understanding of proper GWT coding before I start to tackle bigger apps with more complex UIs.
Also, the proposed structure below is based on 3 GWT principles that Google seems to encourage tirelessly:
- Performance; client-side speed
- Security
- Separation of concerns (MVP, etc.) for ease of testing
One last item here: normally, in my PHP webdev days, I used the concept of "themes/skins" in some of my sites by deploying the site with a different CSS file that simply applied different styling rules to the same DOM elements. Thus I might have a "normal-styles.css
" file that makes the site look "normal", but also have a "facebook-styles.css
" file that gives the site the look-and-feel (colors, fonts, etc.) of Facebook, etc. I'd like to accomplish the same thing here and am proposing a net.bfodder.c2w.client.views.themes
package that will have AbstractTheme
subclasses in it. These subclasses would somehow be injected/baked into the view/presenter at build-time and would give me a way to deploy the app with a different theme or skin. I believe this is the right approach because (if my understanding of ClientBundle
and CssResource
is correct) GWT seems to have object-ified CSS styles into GWT POJOs. My AbstractTheme
subclasses would somehow tie into CssResource
and defined styles for all my Widget
s.
So here is the proposed src/main/java
structure of ClickItToWinIt
(I'm developing in Eclipse 3.7 Indigo with the Google-Eclipse plugin):
ClickItToWinIt/
src/main/java/
net.bfodder.c2w
net.bfodder.c2w.client
MainModule (implements EntryPoint)
AppController
ClientFactory
net.bfodder.c2w.client.views
net.bfodder.c2w.client.views.ui
WinButton (extends Composite implements ClickHandler; hasA TextButton)
LoseButton (extends Composite implements ClickHandler; hasA TextButton)
ImageViewer (extends Composite; hasAn Image)
net.bfodder.c2w.client.views.containers
MainContainer
ButtonContainer
ImageContainer
net.bfodder.c2w.client.views.displays
WinActivity (extends AbstractActivity implements AcceptOneWidget)
LoseActivity (extends AbstractActivity implements AcceptOneWidget)
net.bfodder.c2w.client.views.themes
AbstractTheme
NormalTheme
FacebookTheme --> I really dont't care about FB, just using as an example of a well-known theme/skin
net.bfodder.c2w.client.presenters
IPresenter
ButtonPresenter (extends AbstractActivity implements IPresenter)
ImagePresenter (extends AbstractActivity implements IPresenter)
net.bfodder.c2w.client.history
InitialPlace (corresponds to http://localhost/clickittowinit)
WinPlace (corresponds to http://localhost/clickittowinit/#winning)
LosePlace (corresponds to http://localhost/clickittowinit/#losing)
net.bfodder.c2w.client.events
EventBus impl and GwtEvent impls
net.bfodder.c2w.server
Not worried about server-side for now; I think I "get" that much!
The AppController
is for any oddball app-level business logic that doesn't belong to any one particular IPresenter
subclass. The MainModule
is where everything kicks off (DI, starting preliminary Activity
, etc.). The buttons get placed inside ButtonContainer
, the ImageViewer
gets placed inside the ImageContainer
, and both those inner containers get placed inside MainContainer
(which I assume is created up in MainModule
). As far as layout and style of these UI components, like I said this would be up to whatever the injected/configured AbstractTheme
defines in its rule set.
I'm using the ClientFactory
as recommended here for convenient access to major parts of the app's GWT plumbing.
As for the MVP "stuff", here's what I was thinking:
public class WinActivity extends AbstractActivity {
private final ClientFactory clientFactory;
private final WinPlace place;
public WinActivity(ClientFactory clientFactory, WinPlace place) {
this.clientFactory = clientFactory;
this.place = place;
}
@Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
ImagePresenter presenter = clientFactory.getImagePresenter();
// Inject the presenter with any stateful data here
// presenter.setImage(getWinImageUri());
presenter.go(panel);
}
}
The Question(s)!
Like I said up above, I believe I'm almost there, I'm just having a tough time bridging the gaps on some things. So I ask:
- How do I wire up my
AbstractTheme
subclasses to useCssResource
andClientBundle
so that my app gets injected/configured with the correct theme, and client browser download the correct CSS file (normal-style.css
orfacebook-style.css
)? - Can someone properly explain the relationship between
IPresenter
(from classic MVP),AbstractActivity
, "display regions" (AcceptsOneWidget
) andPlace
s? Is there a 1-to-1-to-1-to1 relation here, or is the intention with GWT to have, say, multiple display regions for everyAbstractActivity
, or some other set of ratios? - How do I connect the
WinPlace
with GWT's History API? So if the user clicksWinButton
,LoseButton
,WinButton
, how to have that saved in browser history so they could hit the forward/back browser buttons and recreate this usage pattern over and over again? - When the
WinButton
gets clicked, it places a some click event on the Event Bus. Where and how do I wire up a handler/listener to the EventBus that will know: (a) to change the image in theImageViewer
, and (b) to store this state change in the History API? - I got the idea for the
AppController
from this article, which unfortunately never provides any code snippets for what theAppController
might actually look like. Can anybody (who is familiar with theAppController
pattern for GWT) shine some light as to what this would look like? Perhaps its somehow anIPresenter
subclass with no particularActivity
or view components? - In general, and I know that these kinds of "amidoinitrite?"-style questions are discouraged on SO, but I have to ask any battle-worn GWT veterans out there: am I way off-base here, or am I more or less on track with GWT best practices? Any suggestions?
I hate to ask such a "super-question" here, but I'd rather do this than clutter SO with 6 medium-sized questions, and just keep repeating the same problem setup over and over again. Plus, with a good code example, it's probably possible to answer all 6 in one fell swoop, so I took a chance here.
Thanks in advance.