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 -~----------~----~----~----~------~----~------~--~---