I've run some quick tests on one approach to incorporating UiBinder,
the MVP design pattern, and child view's. Most of the approaches I've
seen in the group have focused on how to add the child View to the
parent View after construction. But, another approach is to return the
child View from the parent View, and bind the View to the child
Presenter within the parent Presenter.

Here is an example. I have two presenters defined: a MainPresenter and
an AccountListViewer. First, the code for AccountListViewer:

public class AccountListViewer {
  interface Display  {
    ...
  }

  Display display;

  public void bindDisplay( Display display ) {
    this.display = display;

    ...
  }

  ...

}

The MainPresenter's View/Display interface returns an object
implementing AccountListViewer.Display (similar to the HasValue type
characteristic interfaces in Ray's talk). The MainPresenter then uses
this in its constructor to bind the display to the AccountListViewer
object, as shown in the code below:

public class MainPresenter {

  interface Display {
    AccountListViewer.Display getAccountListView();
    ...
  }

  Display display;

  public MainPresenter( AccountListViewer viewer, Display display ) {
    this.display = display;
    viewer.bindDisplay(display.getAccountListView());
  }

  ...

}

Now, here is the MainView.Display implementation:

MainView.ui.xml
-------------------------
<ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'
  xmlns:mr='urn:import:com.berahoff.moneyregirstry.client.accounts'

  ui:defaultLocale="en_us"
  ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
 
ui:generateFormat="com.google.gwt.i18n.rebind.format.PropertiesFormat"
  ui:generateFilename="myapp_translate_source"
  ui:generateLocales="default"
  >
  <g:HTMLPanel>
    <mr:AccountListView ui:field='accountList' />
  </g:HTMLPanel>
</ui:UiBinder>

MainView.java
--------------------------
public class MainView extends Composite implements
MainPresenter.Display {

  interface Binder extends UiBinder<Widget, MainView> {}
  private static final Binder binder = GWT.create(Binder.class);

  @UiField
  AccountListViewer.Display accountList;

  public MainView() {
    initWidget(binder.createAndBindUi(this));
  }

  public Display getAccountListView() {
    return accountList;
  }
  ...
}

Now we have a nice declarative layout of our Widgets according to the
parent without having to force the Display interfaces to really
understand what a Widget is. Note, you should refrain from injecting
child Presenters with a Display instance in this case to avoid
unnecessary object creation.

Some benefits to this approach is that it could allow one set of
developers/designers to work on the View/Display portion of the
application while others work on Presenter logic. Also, changing the
way the UI looks is simply a change to the associated Views and
injections. The one downside  I see with this approach is the coupling
between the parent and child View's, however since there is already a
coupling of the parent and child Presenters, this doesn't seem like a
major issue.

Hope this helps some thoughts. I'm still interested to see Ray Ryan's
example once he gets some time to put it together. Hopefully it will
address some of these issues.

-Jason

On Aug 28, 7:11 am, Etienne Neveu <nev...@gmail.com> wrote:
> (Addendum: Thomas's answer beat me to it, but since I noticed it after
> writing this post, I will post my answer anyway ;) )
>
> I did not have a dev environment setup when I posted this, so did not
> test this. I obviously should have, because I did not see the
> recursion in what I was trying to do.
>
> As you guessed it, the problem is that we want to inject the same
> Display interface (MyPresenter.Display) in two different places:
> - in the view, where we want Gin to use the @Provides factory method
> (which creates a new view, injects it into a new presenter, and
> returns the view)
> - in the presenter, where we do NOT want to call the @Provides method,
> but instead bind MyPresenter.Display to MyPresenterView in the
> "standard" way.
>
> Guice's private modules would have solved this problem elegantly but
> they are not yet available in 
> Gin:http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/injec...
>
> Here is what I did instead:
>
> I took as a basis the example project from this good gwt-presenter
> tutorial:http://blog.hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-...
>
> I created a new Guice binding annotation:
>
> @BindingAnnotation
> @Target( { FIELD, PARAMETER, METHOD })
> @Retention(RUNTIME)
> public @interface ViewForPresenter {
>
> }
>
> ----------------
>
> I created a new presenter:
>
> public class TestPresenter extends
> WidgetPresenter<TestPresenter.Display> {
>     public interface Display extends WidgetDisplay {
>         HasClickHandlers getTestButton();
>     }
>
>     // Notice the @ViewForPresenter annotation here
>     @Inject
>     public TestPresenter(@ViewForPresenter Display display, EventBus
> eventBus) {
>         super(display, eventBus);
>         Log.info("TestPresenter created");
>         bind();
>     }
>
>     @Override
>     protected void onBind() {
>         registerHandler(display.getTestButton().addClickHandler(new
> ClickHandler() {
>             @Override
>             public void onClick(ClickEvent event) {
>                 Window.alert("Button clicked!");
>             }
>         }));
>     }
>
>     @Override
>     protected void onUnbind() {
>     }
>
>     public void refreshDisplay() {
>     }
>
>     public void revealDisplay() {
>     }
>
>     public static final Place PLACE = null;
>
>     @Override
>     public Place getPlace() {
>         return PLACE;
>     }
>
>     @Override
>     protected void onPlaceRequest(final PlaceRequest request) {
>     }
>
> }
>
> ----------------
>
> And its view:
>
> public class TestView extends VerticalPanel implements
> TestPresenter.Display {
>
>     private final Button testButton;
>
>     @Inject
>     public TestView() {
>         Log.info("TestView created");
>         testButton = new Button("Test");
>         add(testButton);
>     }
>
>     @Override
>     public HasClickHandlers getTestButton() {
>         return testButton;
>     }
>
>     public Widget asWidget() {
>         return this;
>     }
>
>     @Override
>     public void startProcessing() {
>     }
>
>     @Override
>     public void stopProcessing() {
>     }
>
> }
>
> ----------------
>
> Updated the module:
>
> public class GreetingClientModule extends AbstractPresenterModule {
>
>     @Override
>     protected void configure() {
>         [...]
>
>         bind(TestPresenter.class);
>         // First binding for TestPresenter.Display, which will be used
> when we use the @ViewForPresenter annotation
>         bind(TestPresenter.Display.class).annotatedWith
> (ViewForPresenter.class).to(TestView.class);
>     }
>
>     // Second binding for TestPresenter.Display, which will be used
> when no annotation is present
>     @Provides
>     TestPresenter.Display provideTestView(TestPresenter testPresenter)
> {
>         testPresenter.bind();
>         return testPresenter.getDisplay();
>     }
>
> }
>
> ----------------
>
> And modified the GreetingView to inject TestView and show it:
>
> public class GreetingView extends Composite implements
> GreetingPresenter.Display {
>     [...]
>
>     @Inject
>     public GreetingView(TestPresenter.Display testView) {
>         Log.info("GreetingView created");
>         final FlowPanel panel = new FlowPanel();
>         panel.add(testView.asWidget());
>
>         [...]
>
> ----------------
>
> And voila:
>
> 2009-08-28 13:48:10,068 [INFO ] TestView created
> 2009-08-28 13:48:10,120 [INFO ] TestPresenter created
> 2009-08-28 13:48:10,131 [INFO ] GreetingView created
> 2009-08-28 13:48:10,219 [INFO ] GreetingPresenter created
>
> Now, if you inject TestView anywhere in your application, it will
> instantiate a new Presenter/View.
>
> Regards,
>
> Etienne
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to