MVP with EventBus question

52 views
Skip to first unread message

Paul Schwarz

unread,
Jun 23, 2010, 4:04:24 PM6/23/10
to Google Web Toolkit
Working with the MVP pattern, or more like the VP pattern at this
point, I have:
MainPagePresenter
MainPageView
WidgetA
WidgetB

... so imagine that WidgetA and WidgetB will be attached to
MainPageView.

Using Gin I have an EventBus injected into the MainPagePresenter. I
can then add click handlers that place an event on the EventBus from
within my MainPagePresenter. This might look like:

getMainPageView().getSendButton().addClickHandler(new ClickHandler(){
public void onClick(ClickEvent event) {
eventBus.fireEvent(new SendEvent());
}
}

But now let's say that WidgetA and WidgetB actually have quite a few
user interactions, you'll end up with a lot of methods that look like
the one above sitting in your Presenter class.
1. Is this correct?
Or
2. should the Presenter hand the reference to the EventBus through to
its View, who may even hand it through to the Widgets so that they can
talk directly to the EventBus?

If the second option is the better option from an architecture/
separation of concerns point of view, then what is the best way to
hand the reference over to the View in such a way that keeps the
coupling between Presenter and View as loose as possible?

Note, currently my Presenter defines a View interface which my View
implements, so the coupling is loose but based upon this interface.

Paul Schwarz

unread,
Jun 23, 2010, 5:03:51 PM6/23/10
to Google Web Toolkit
To answer my own question:

1. Gin can be used to inject the EventBus into the View as well as the
Presenter, so now our View has a reference to the EventBus

2. In order to give the EventBus to the Widgets "owned" by the View
those Widgets will require a constructor argument which will be the
EventBus instance. UiBinder will fail if it doesn't find a default
constructor, except that they have provided some nice work arounds.
The appropriate workaround in this case is to provide a widget
factory. Notice the use of @UiFactory in the example below:

public class UserDashboard extends Composite {
interface MyUiBinder extends UiBinder<Widget, UserDashboard>;
private static final MyUiBinder uiBinder =
GWT.create(MyUiBinder.class);

private final String[] teamNames;

public UserDashboard(String... teamNames) {
this.teamNames = teamNames;
initWidget(uiBinder.createAndBindUi(this));
}

/** Used by MyUiBinder to instantiate CricketScores */
@UiFactory CricketScores makeCricketScores() { // method name is
insignificant
return new CricketScores(teamNames);

Filipe Sousa

unread,
Jun 23, 2010, 7:18:17 PM6/23/10
to Google Web Toolkit
I believe you can use @UiField(provided=true):

@UiField(provided=true) Foo foo

@Inject
UserDashboard(Foo foo) {
this.foo = foo

Paul Schwarz

unread,
Jun 23, 2010, 8:13:25 PM6/23/10
to Google Web Toolkit
I looked into that, and (unless I'm wrong), I think that
@UiField(provided=true) will cause the UiBinder to look in the .ui.xml
file for argument to satisfy Foo. In my case I am trying to "inject"
an EventBus into the widget, not a visual element.

PhilBeaudoin

unread,
Jun 24, 2010, 10:35:38 AM6/24/10
to Google Web Toolkit
In gwt-platform (http://code.google.com/p/gwt-platform/) we use gin to
inject the EventBus to whoever needs it. The framework is designed
such that only the presenters communicate on it.

In your example, the gwt-platform way would be to create a
PresenterWidget for WidgetA and another for WidgetB. This way they act
like widgets, but have a nice testable Presenter layer that can be
used to decouple interactions via the EventBus. If your custom
presenter for WidgetA has too many of these "addClickHandler" methods,
you can break things down further, getting an entire hierarchy of
PresenterWidget.

Hope it helps,

Philippe

Andrew Hughes

unread,
Jun 25, 2010, 11:38:09 AM6/25/10
to google-we...@googlegroups.com
Yeah, the one problem with UiBinder and MVP is this pattern collision....

UiBinder says, "view are directly attached to views".

MVP says, "view's are attached to presenters, if you want to chain views the presenters control this".

Consequently, you can't get UiBinder to create your @UiFields (i.e. empty constructor) and you can't get Gin to @Inject them into the view either... because they are in the presenter. Unless of course you use @Named+Singleton bindings in Gin and then both presenter and view will be injected with the same object.

This is the one bit of boilerplate we're still writing. 


--
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-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.


Paul Schwarz

unread,
Jun 26, 2010, 7:08:38 AM6/26/10
to Google Web Toolkit
Ok, I didn't implement it this way, but would prefer to.

At the moment I have 3 mother-sized presenters + views. These have
EventBus injected into them by gwt-platform. Then the widgets that are
"owned" by this view are then constructed manually. I think I can
still test them atomically but I'm not sure... the concepts are still
a little hazy, learning UiBinder, Mocking and GWTP at the same time
causes some head spin.

My question now is when is it appropriate to make a new Presenter? and
how does a PresenterWidget compare? I'm confused because in GWTP a
presenter has a Place, so it seems like Presenters are "virtual" pages
in an ajax app. But now you're saying to make Presenters for all my
widgets individually... or is this where PresenterWidget comes in?

At the moment things are scruffy because my presenter gets an EventBus
injected, but then has to bubble it up through @UiFactory constructors
into the owned Widgets.

PhilBeaudoin

unread,
Jun 27, 2010, 12:48:55 AM6/27/10
to Google Web Toolkit
I answer to this earlier, but it somehow got gobbled by Google Groups.
If you see it around, let me know. ;)

Re: Complex widgets and PresenterWidgets

You're exactly right. PresenterWidgets are GWTP's way of doing a
complex widget with a nice testable class. Your PresenterWidget has
all the widget's logic and gets injected with the EventBus (and the
dispatcher if needed, or other services). The view is the complex
widget itself.

Re: Presenters vs PresenterWidget

Presenters are singletons. They are totally decoupled from one another
and communicate only via the Event bus. They can be organized
hierarchically, but the hierarchical structure is very loose: parent's
don't know their children in advance, and child don't know their
parent. The hierarchy gets sets up entirely via the EventBus. The
lowest-level Presenters of this hierarchy (so-called leaf Presenters)
need to be attached to a Place. They are (lazily) instanciated
whenever their history token is invoked, and at that point they set-up
the hierarchy.

PresenterWidget, on the other hand, are not singletons. Also, they are
meant to be injected in their parent Presenter (a bit like you would
expect complex widgets to contain one another). They are never
attached to a place: they get setup when their containing presenter is
set-up. Their parent presenter can communicate with them through
method invocation if desired, it does not have to use the event bus.
Really, PresenterWidget can be thought of as "widget with a testable
layer".

A typical complex page in a GWTP app might look something like:

PresenterA (splits the page in a header and a main area )
|
+-- HeaderPresenter
|
+-- MainContentPresenter
|
+--> ClientListPresenterWidget
|
+--> CliendDetailPopupPresenterWidget

Where:
"--" denotes an indirect relationship (via the event bus)
"-->" denotes an owned relationship (typically injected with GIN)

There are some examples of the sort on the gwt-platform website, you
may want to check them out.

Cheers,

Philippe

Paul Schwarz

unread,
Jun 28, 2010, 12:09:16 PM6/28/10
to Google Web Toolkit
Thanks so much for this answer. I have printed it and pinned it up
right beside my screen so will be referring to it often until these
concepts become solid in my mind. I think the largest mind gap here
for me is the concept that a presenter's view is used to fill the
whole screen, and then it has children presenter+view but the parent
doesn't explicitly create or know about them!? I'm getting my head
around these concepts slowly. Thanks for the info.

Just a question for now though. An example really. Say you have an app
with:
- main screen
- settings screen

It also has a login panel that is essentially a singleton and popups
up from time to time when your session expires, no presenter really
owns it, it just lives in a void until your session expires.

Now, your main screen should be accessible at /#main and your settings
at /#settings.

And lastly, your main page a header that is constantly present at the
top of the page, but then in the main area it is like the "Expenses"
example. i.e. it is a table of rows of data, but if you click on one
of the rows it slides across to reveal more data for that row you
clicked. So the main table would be available only at /#main, but
then /#main;item=20 (or should it be /#item;id=20?) would slide across
to reveal the details page for item 20. Then hitting /#main would
slide you back to the main table.

You can see from this example that there are a variety of Places, page
states and widgets usages. Would you be so kind as to explain where
you would apply Presenters, PresenterWidgets, etc and how you should
handle the history tokens in order to give this app coherent "state"
based on history, but optimal usage of GWTP in terms of correct MVP
patterns (which then assist greatly with JUnit testing!).

Paul Schwarz

unread,
Jun 29, 2010, 8:29:27 AM6/29/10
to Google Web Toolkit
I have totally restructured my app to follow the "page setup by event"
type idea that you've suggested in your email. I have been through all
four samples in the samples repo but can't answer my own question.

I have set up based on the "nested sample" but now I have a
HeaderPresenter like you have in your email, present on all pages,
Home, AboutUs, ContactUs. So essentially it's a singleton and will
never be removed/replaced.

Ok, now I see how you have this idea in your "leaf" presenters:
@Override
protected void revealInParent() {
RevealContentEvent.fire(eventBus, MainPresenter.TYPE_SetMainContent,
this);
}
... but now, what about the Header? I can't get it to be injected or
revealed. I'm not sure what the correct way is (maintaining loose
coupling). Currently I have:
@Override
protected void revealInParent() {
RevealContentEvent.fire(eventBus,
MainPresenter.TYPE_SetHeaderContent, this);
}
... in my HeaderPresenter, and then in my MainPagePresenter I have:
@ContentSlot
public static final Type<RevealContentHandler<?>> TYPE_SetMainContent
=
new Type<RevealContentHandler<?>>();

@ContentSlot
public static final Type<RevealContentHandler<?>>
TYPE_SetHeaderContent =
new Type<RevealContentHandler<?>>();

... and then in my MainPageView:

@Override
public void setContent(Object slot, Widget content) {
if (slot == MainPresenter.TYPE_SetMainContent) {
setMainContent(content);
} else if (slot == MainPresenter.TYPE_SetHeaderContent) {
setHeaderContent(content);
} else {
super.setContent(slot, content);
}
}

private void setHeaderContent(Widget content) {
mainHeaderPanel.clear();

if (content != null)
mainHeaderPanel.add(content);

}

private void setMainContent(Widget content) {
mainContentPanel.clear();

if (content != null)
mainContentPanel.add(content);
> > > > > > > > implements, so the coupling is loose but...
>
> read more »

PhilBeaudoin

unread,
Jun 29, 2010, 2:14:56 PM6/29/10
to Google Web Toolkit
Hi Paul,

We discussed this via IM but for the benefit of other readers I'll
summarize it here... The samples in the gwt-platform are relatively
simple and meant more as a showcase of the different features than as
full applications. On the other hand my (main) side-project,
PuzzleBazar (http://code.google.com/p/puzzlebazar/) makes heavy use of
GWTP and has examples that are fairly close to what you want here.

Going back to your example...

Re: The header
You can make the header a PresenterWidget and inject it into its
parent presenter. Then if you use the slot mechanism of GWTP, this
HeaderPresenterWidget will correctly receive all the lifecycle events.
For an example of this take a look at TYPE_RevealTopBarContent in:
http://code.google.com/p/puzzlebazar/source/browse/src/com/puzzlebazar/client/core/presenter/PagePresenter.java

Re: The item details page
I would use `/#item;id=20` Although making the page slide is a bit
trickier. I haven't given that too much thought. The problem is that
the history token can be accessed directly (i.e. a bookmark) in which
case you can't really use scrolling. My guess wrt page transitions is
that they would be a bit difficult to implement without diving in the
internals of gwt-platform. Don't hesitate to discuss this on the GWTP
forum (http://groups.google.com/group/gwt-platform). This could turn
out to be a cool feature to add...

Cheers,

Philippe
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages