Menu Item and MVP

238 views
Skip to first unread message

mic

unread,
Mar 31, 2010, 2:07:33 AM3/31/10
to Google Web Toolkit
Just sharing some thoughts....

Would the MenuItem class need to implement HasClickHandler so that
when a MenuItem is clicked, the action can be handled in the
presenter. At this time, the MenuItem needs an object that implements
the Command interface, so the view seems to know about the presenter.

I would like to know what others think about this.

- mic.

Nathan Wells

unread,
Mar 31, 2010, 8:05:58 AM3/31/10
to Google Web Toolkit
I created an "EventFiringCommand" class for instances like this.
Basically, you put the event bus and the event to fire (or the code to
generate the event) in the command. The Command should be a presenter-
layer object, since it has no need to know about the view (and should
be easy to unit test).

Theoretically, you could just make inner classes in a Presenter for
each Command you write. It all depends on how you want to organize
your code and the needs of your specific project.

Thomas Broyer

unread,
Mar 31, 2010, 10:14:18 AM3/31/10
to Google Web Toolkit

I've recently migrated from views with many HasXxxHandlers getters to
views with a setListener method that the presenter "injects" into its
view. The listener defines "callback methods" such as save(), close(),
italicize(), bold(), delete(), etc.
It makes unit tests waaay easier to write (you don't have to mock each
HasXxxHandlers, capturing each added XxxHandler, eventually mocking
the XxxEvent, etc.) and in your case would make things easier: the
Command is an implementation detail of the view.

Nathan Wells

unread,
Mar 31, 2010, 7:15:27 PM3/31/10
to Google Web Toolkit
Thomas,

I've heard of your approach before, but I'm not clear on the
implementation... are you passing the presenter itself into the view?
or are you passing instances of event handlers? The difference seems
trivial, but in practice (as I imagine it, anyway) seems large:

http://etherpad.com/sIAaiCfuiG

Let me know what you think. I would assume the first way is what your
referring to in your comment above, which implies that it is a View
layer responsibility to deal with HandlerRegistrations and the like.
Is that correct?

Ian Bambury

unread,
Mar 31, 2010, 9:34:03 PM3/31/10
to google-we...@googlegroups.com
I've been looking into this for a chapter in the new edition of GWT In Action, here's where I'm at.

The Google I/O approach to MVP has never sat well with me because, apart from the fact that the interface can get out of hand, it just seems wrong that the interface specifies that the view must, say, have something clickable and not leave the implementation to the view.

So what I'm playing about with right now is (for the example you gave).

There would be a couple of interfaces HasClose and HasSave which would be along the lines of 

public interface HasSave
{
    void save();
}

There would be a couple of corresponding CloseHandler and SaveHandler classes e.g.

class SaveHandler implements ClickHandler
{
    private HasSave presenter;

    public SaveHandler(HasSave presenter)
    {
        this.presenter = presenter;
    }
    @Override
    public void onClick(ClickEvent event)
    {
        presenter.save();
    }
}

The presenter looks like this
class Presenter implements HasSave, HasClose
{
    View view;
    
    public Presenter(View view)
    {
       this.view = view;
    }
    @Override
    public void save()
    {
        view.setText("Save");
    }

    @Override
    public void close()
    {
        view.setText("Close");
    }
}

and the view like this

class View extends FlowPanel implements HasText
{
    Label label = new Label();
    public View()
    {
        Presenter presenter = new Presenter(this);
        add(new Button("Open", new SaveHandler(presenter)));
        add(new Button("Close", new CloseHandler(presenter)));
        add(label);
    }
    @Override
    public String getText()
    {
        return label.getText();
    }
    @Override
    public void setText(String text)
    {
        label.setText(text);
    }
}

in the AppController or onModuleLoad method, you'd just have the line

RootPanel.get().add(new View());

Does that make sense? Feel free to ask but I'm still not too far forward with this approach yet.

For a tiny project, it's a bit of work (unless you have already written the interfaces and handler classes for some other project) but it does keep everything well organised in larger projects.

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


Ian Bambury

unread,
Mar 31, 2010, 9:41:32 PM3/31/10
to google-we...@googlegroups.com
That first button should say "Save" - It's late here :-)

Thomas Broyer

unread,
Apr 1, 2010, 6:16:47 AM4/1/10
to Google Web Toolkit

On Apr 1, 1:15 am, Nathan Wells <nwwe...@gmail.com> wrote:
> Thomas,
>
> I've heard of your approach before, but I'm not clear on the
> implementation... are you passing the presenter itself into the view?
> or are you passing instances of event handlers? The difference seems
> trivial, but in practice (as I imagine it, anyway) seems large:
>
> http://etherpad.com/sIAaiCfuiG
>
> Let me know what you think. I would assume the first way is what your
> referring to in your comment above, which implies that it is a View
> layer responsibility to deal with HandlerRegistrations and the like.
> Is that correct?

Yes. It makes a few things much easier for me:
- I can use @UiHandler in the view, which makes the whole thing more
readable (on the down-side, I used to create a class that implement
all required handlers instead of using an anonymous class per handler;
UiBinder doesn't do this optimization (yet?) with @UiHandlers)
- I can use event delegation in the view
- The view methods are more "consistent" (get/setUserName and
setUserNameEnabled(boolean), vs. getUserName->get/setText and
setUserNameEnabled(boolean); this elad people to ask for a HasEnable
interface, which wouldn't solve anything as you'd still need
getUserName->HasText and getUserNameEnablable->HasEnable), even more
when also dealing with non-widget UI elements: I implemented a login
form with a <form> in the HTML page which action="" is set to callback
a GWT method ($wnd.__form_callback = $entry(view.@....onSubmit())) and
had to create a dummy HasClickHandlers to return as the
"HasClickHandlers submitButton()"; with a Listener is way easier: the
onSubmit just calls back the listener.submit().
- I have one "screen" where the user can add files to upload, each
"file" is a line composed of a FileUpload and a "cancel" button next
to it that removes the "line" from the screen (and therefore "cancels
the file" before the form is submitted), already uploaded files
(unpredictable number) are shown as a label with a "delete" button
next to it. Add to this that files are grouped into dynamic,
unpredictable "groups". Previously, I did everything with a
SelectionEvent<String> where the value was something like "add:<file
group>", "cancel:<file id>", or "delete:<file id>" (I use event
delegation in the view). Now, instead of firing a SelectionEvent with
a specific value, I just call the listener's addFile(String group),
cancelFile(String fileId) or deleteFile(String fileId).
Sure I could have defined new events instead of doing it all through a
SelectionEvent but it wouldn't have make things much easier, with more
files to maintain.

As for the implementation, I currently have something like:
public class FooPresenter {
@ImplementedBy(FooView.class)
public interface Display {
void setListener(Listener listener);

void setThisVisible(boolean visible);

void setThatEnabled(boolean enabled);
}

public interface Listener {
void save();

void close();
}

private class Handlers implements PlaceHandler,
SomeOtherBusEventHandler, Listener {
...
}

@Inject
public FooPresenter(Display view, EventBus bus) {
Handlers handlers = new Handlers();
bus.addHandler(PlaceEvent.getType(), handlers);
bus.addHandler(SomeOtherBusEvent.getType(), handlers);
view.setListener(handlers);
}

...
}

public class FooView implements FooPresenter.Display {
@UiField Button save;
@UiField Button close;

private FooPresenter.Listener listener;

...

public void setListener(FooPresenter.Listener listener) {
assert listener != null;
this.listener = listener;
}

@UiHandler("close")
void onCloseClick(ClickEvent event) {
listener.close();
}

@UiHandler("save")
void onSaveClick(ClickEvent event) {
listener.save();
}
}

I'm using a Listener *interface* so the view doesn't have direct
dependencies on the presenter's behavior (I could therefore mock a
Listener and make a small app to "manually test" my view without
actually having any presenter, event bus, etc.)

I decided to switch to this design instead of HasXxxHandlers when I
saw Ray's work on RequestFactory: """In the Wave style: the view
(editor) has a single listener for its semantic operations."""
The main difference is that I do not have an interface for each
presenter with its impl class (interface FooPresenter / class
FooPresenterImpl implements FooPresenter, FooPresenter.Listener) so my
presenter class doesn't itself implement the listener interface (it
would also "pollute" its public API, which is not a problem in Ray's
case where no other object has a direct dependency on the Impl class,
only on the presenter interface).
In a new project we're starting, I might reconsider this approach
though, and introduce an interface vs. impl class dichotomy all over
the place.

Something to be aware of though: the Listener approach puts a bit more
responsibility into the view than the HasXxxHandlers approach, but
it's so much easier to work with (and write unit tests) that's I think
it's worth it.

mic

unread,
Apr 5, 2010, 12:29:04 PM4/5/10
to Google Web Toolkit
Nathan,

I think it does not clearly separate out the View and Presenter using
interfaces. This is the reason why I moved away from using menus.
I would prefer if there was a HasMenuItemClicked which returns the
menu item and the presenter would have code like

this.display.getABCMenuItem().addClickHandler() ( new ClickHandler() {
public void onClick(MenuItemClickEvent event) {
doAction();
}
});

I think this is much cleaner...

mic

On Mar 31, 5:05 am, Nathan Wells <nwwe...@gmail.com> wrote:
> I created an "EventFiringCommand" class for instances like this.
> Basically, you put the event bus and the event to fire (or the code to
> generate the event) in the command. The Command should be a presenter-
> layer object, since it has no need to know about the view (and should
> be easy to unit test).
>
> Theoretically, you could just make inner classes in a Presenter for
> each Command you write. It all depends on how you want to organize
> your code and the needs of your specific project.
>

mic

unread,
Apr 5, 2010, 12:34:39 PM4/5/10
to Google Web Toolkit
Ian,

I feel the Google IO MVP approach is good because interfaces are used
to abstract the interaction between view and presenter. IMHO, the view
containing the presenter or the presenter knowing about the view makes
replacing presenters and views extremely difficult especially if you
do want to target desktop and mobile apps.

-mic

> > google-web-tool...@googlegroups.com<google-web-toolkit%2Bunsu...@googlegroups.com>

mic

unread,
Apr 5, 2010, 12:44:34 PM4/5/10
to Google Web Toolkit
Thomas,

I need to think more about this approach. Ray mentioned in his talk
about having too many events and performance. Does your approach not
lead to that?

-mic

Reply all
Reply to author
Forward
0 new messages