GWT MVP: Connecting the Dots on Presenters, Activities, Displays & Places

354 Views Asked by At

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:

enter image description here

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 Widgets.

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:

  1. How do I wire up my AbstractTheme subclasses to use CssResource and ClientBundle so that my app gets injected/configured with the correct theme, and client browser download the correct CSS file (normal-style.css or facebook-style.css)?
  2. Can someone properly explain the relationship between IPresenter (from classic MVP), AbstractActivity, "display regions" (AcceptsOneWidget) and Places? Is there a 1-to-1-to-1-to1 relation here, or is the intention with GWT to have, say, multiple display regions for every AbstractActivity, or some other set of ratios?
  3. How do I connect the WinPlace with GWT's History API? So if the user clicks WinButton, 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?
  4. 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 the ImageViewer, and (b) to store this state change in the History API?
  5. I got the idea for the AppController from this article, which unfortunately never provides any code snippets for what the AppController might actually look like. Can anybody (who is familiar with the AppController pattern for GWT) shine some light as to what this would look like? Perhaps its somehow an IPresenter subclass with no particular Activity or view components?
  6. 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.

0

There are 0 best solutions below