On Apr 1, 1:15 am, Nathan Wells <[email protected]> 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([email protected]())) 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.

-- 
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-web-toolkit?hl=en.

Reply via email to