Hi, all,
Like many other projects, our GWT app needed a general framework. We
opted to create one, and since I haven't seen anything else that works
exactly like ours, I thought I'd share what we've done, in the hopes
that doing so might contribute something useful to the (already
extensive) discussions on GWT MVP frameworks. We'd also love to hear any
comments/criticisms/suggestions you may have...!
In a nutshell:
- The framework is component-based. (No hot-swapping of components,
though---code must be recompiled in order to switch components in or out.)
- Almost all objects are created in an injected context using GIN.
- We extend the standard GWT Place, Activity and ActivityManager
classes/interfaces (among others).
- There are two kinds of components in the framework: those that provide
activity/view/ui-region bindings for specific places, and those that
provide other kinds of functionality.
- Activities are a generified extension of GWT's Activity; Activity
classes have generic parameters that associate them with a specific
Place class, a View class, and a class that represents the component
they're a part of.
I'm not sure there's less boilerplate than in other MVP approaches. At
least, though, it seems like everything is together that goes together,
and we've got a decent, though very basic, path for encapsulating
functionality in components as we complete and extend our app. And we
got rid of the infamous "if (place instanceof SomePlace)" chain in
ActivityMapper!
Anyone who'd like to check it out can get it like this:
$ git clone http://lais.mora.edu.mx/gitrepo/pescador.git/
$ cd webclient (note: other subdirectories of the repository have
unrelated stuff that you'll probably want to ignore)
Following are a few code snippets.
Here's part of a simple activity:
public class BodyStartPageActivityImpl
extends WebClientActivityBase<BodyStartPageView, StartPagePlace,
StartPagePAVComponent>
implements BodyStartPageActivity {
@Inject
public BodyStartPageActivityImpl(
@Assisted StartPagePlace place) {
super(place);
}
@Override
public void start(AcceptsOneWidget container, EventBus eventBus) {
BodyStartPageView view = getView();
view.setText("Body start page activity here...<br />Another beautious
line of start page.");
container.setWidget(view);
}
[...]
}
As shown, the activity is created using GIN, so it can have almost
anything injected via its constructor. It also gets access to the
correct view and place instances.
Here's a bit of a component that sets up activity/view/ui-region
bindings for a place:
public class ContentPAVComponentImpl extends PlaceActivityViewComponentBase<
ContentPAVComponent,
ContentPlace>
implements
ContentPAVComponent {
@Inject
public ContentPAVComponentImpl(
ContentPlaceProvider contentPlaceProvider,
ActivitiesFactory<ContentPlace, HeadContentActivity>
headActivitiesFactory,
ActivitiesFactory<ContentPlace, BodyContentActivity>
bodyActivitiesFactory,
ActivitiesFactory<ContentPlace, BannerContentActivity>
bannerActivitiesFactory,
ActivitiesFactory<ContentPlace, WestContentActivity>
westActivitiesFactory) {
super(
ContentPAVComponent.class,
"contenido",
contentPlaceProvider,
ContentPlace.class);
// set up regions and activities factories
addRegionAndActivitiesFactory(Head.class, headActivitiesFactory);
addRegionAndActivitiesFactory(Body.class, bodyActivitiesFactory);
addRegionAndActivitiesFactory(Banner.class,
bannerActivitiesFactory);
addRegionAndActivitiesFactory(West.class, westActivitiesFactory);
}
public static class ContentGinModule extends AbstractGinModule {
// GIN module for bindings specific to this component
[...]
}
[...]
}
The component has a place provider and (GIN-generated) activity
factories injected, which are passed along to the superclass. In turn,
the superclass and other parts of the framework take care of starting
the appropriate activities and views in the appropriate ui regions when
the app goes to a place of the specified class. GIN bindings specific to
this component are set up in the component's own GIN module.
Finally, here's a bit of a ComponetSetup class, which ties everything
together:
public class ActiveComponentSetup extends ComponentSetup {
/**
* Activate all necessary {@link GinModule}s for DI in the components
to use,
* as well as for basic infrastructure and local GinModule.
*/
@GinModules({
ActiveComponentSetupGinModule.class, // current setup
module, included below
WebClientGinModule.class, // required
basic infrastructure
StandardDispatchModule.class, // required
basic infrastructure
ContentGinModule.class, //
content component
StartPageGinModule.class //
start page component
})
public interface ActiveComponentSetupGinjector extends
WebClientGinjector {}
/**
*<p>Sets specific bindings;
* Backreferences (from components back to {@link
ActiveComponentSetup})
* are set, as necessary, as components are received.</p>
*
*<p>Also set the root region provider.</p>
*
*<p>Note: components that rely on automatic generation of
internationalized
* {@link Messages} by Maven must also set up the appropriate
configuration in the
* pom.xml.</p>
*/
@Inject
public ActiveComponentSetup(
WindowLayout winLayout, //
global window layout widget
ContentPAVComponent contentPAVBinding, // general
content component
StartPagePAVComponent startPagePAVBinding // start
page component
) {
super();
// do this before registering components, so that PAVBinding
components
// can be checked against the regions available here
setRootRegionsWidget(winLayout);
// register components
// TODO once multibindings and mapbindings are in Gin, look
into using that
addComponents(contentPAVBinding, startPagePAVBinding);
setDefaultPlaceProvider(startPagePAVBinding);
}
[...]
}
So, basically, we have our Ginjector which brings together all the
required GIN modules. ComponentSetup's constructor gets via injection
the components we'll activate, and sets the default place and the object
that'll take care of global UI layout.
So at the entry point, all we have to do is this:
public class WebClient implements EntryPoint {
/**
* This is the entry point method.
*/
public void onModuleLoad() {
WebClientGinjector ginjector =
GWT.create(ActiveComponentSetupGinjector.class);
ginjector.getComponetSetup().start();
}
}
And that's that! Many thanks in advance for your comments, suggestions,
scathing criticisms, etc.!
Take care,
Andrew
P.S. It's all GPL, so anyone is welcome to reuse this, too.
--
You received this message because you are subscribed to the Google Groups "Google
Web Toolkit" group.
To post to this group, send email to google-web-toolkit@googlegroups.com.
To unsubscribe from this group, send email to
google-web-toolkit+unsubscr...@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/google-web-toolkit?hl=en.