Hi, *WALL OF TEXT BEGINS ;) *
We also encountered this problem in our app. In our case, our root presenter did not need to hold references to our child presenter(s). In this case, you only need to solve the problem you mentioned: "If ContactView has EmailView injected, we get into a dilemma where we don't have the Presenter instantiated". So I did something like this: ------------------------- We are using gwt-presenter. If you extend "WidgetPresenter" ( http://code.google.com/p/gwt-presenter/source/browse/trunk/src/main/java/net/customware/gwt/presenter/client/widget/WidgetPresenter.java ), you can do the following in your view: public class ContactView extends VerticalPanel implements ContactPresenter.View { private final Provider<EmailPresenter> emailPresenterProvider; @Inject public ContactView(Provider<EmailPresenter> emailPresenterProvider) { emailPresenterProvider = emailPresenterProvider; } private Widget createEmailView() { return emailPresenterProvider.get().getDisplay().asWidget(); } private void buildWidget() { add(new HtmlPanel("Hi there"); // Create 3 different email views, with 3 different presenters instantiated add(createEmailView()); add(createEmailView()); add(createEmailView()); } } The problem is that you make your views aware of the presenters. But since you unit-test the presenters, it won't pose a problem while unit- testing. Another solution if you want to abstract the presenter from the view, would be to use a provider method in your GIN module: @Provides EmailWidget createView(EmailPresenter presenter) { return presenter.getDisplay().asWidget(); } and then inject a Provider<EmailWidget> in your view. Each call to emailWidgetProvider.get() will then create a new presenter (which gets injected with a new view), give it to the provider method, and return the associated widget to be used by the view. This solves the problem of "not having the presenter instantiated". ------------------------- Now, this "simple case" works because we supposed our ContactPresenter does not need to access the EmailPresenters. You can avoid these kind of dependencies by communicating between the presenters through the eventbus. But you won't always be able to do so, and in the real world you will often want to be able to call methods on your child presenters. I think I would do something like this: public class ContactPresenter extends WidgetPresenter<ContactPresenter.View> { public static interface View extends WidgetDisplay { void add(WidgetDisplay display); void remove(WidgetDisplay display); } private final Provider<EmailPresenter> emailPresenterProvider; private Contact contact; private Map<Email, EmailPresenter> emailMap = new HashMap<Email, EmailPresenter>(); public ContactPresenter(ContactView display, EventBus eventBus, Provider<EmailPresenter> emailPresenterProvider, ContactModel contactModel) { super(display, eventBus); this.emailPresenterProvider = emailPresenterProvider; } void onBind() { registerHandler(eventBus.addHandler(EmailAddedEvent.getType(), new EmailAddedHandler() { void onEmailAdded(EmailAddedEvent event) { // should this ContactPresenter handle this email? if (contact.equals(event.getContact())) { addEmail(event.getEmail()); } } })); registerHandler(eventBus.addHandler(EmailRemovedEvent.getType(), new EmailRemovedHandler() { void onEmailRemoved(EmailRemovedEvent event) { // should this ContactPresenter handle this email? if (contact.equals(event.getContact())) { removeEmail(event.getEmail()); } } })); } void addEmail(Email email) { EmailPresenter emailPresenter = emailPresenterProvider.get(); emailPresenter.setEmail(email); emailMap.put(email, emailPresenter); display.add(emailPresenter.getDisplay()); } void removeEmail(Email email) { EmailPresenter emailPresenter = emailMap.get(email); display.remove(emailPresenter.getDisplay()); emailPresenter.destroy(); emailMap.remove(email); } void onUnbind() { // handlers are de-registered by BasicPresenter superclass // since we called registerHandler() in the onBind() method for (Email email : emailMap.keySet()) { removeEmail(email); } } // Should be injected in the constructor with assisted inject or with a // factory? void setContact(Contact contact) { this.contact = contact; for (Email email : contact.getEmails()) { addEmail(email); } } } class ContactView extends VerticalPanel implements ContactPresenter.View { // Create the view and so on... void add(WidgetDisplay display) { add(display.asWidget()); } void remove(WidgetDisplay display) { remove(display.asWidget()); } } (some things might be missing, since I wrote that without any dev environment... but the main ideas are there) ------------------------- All of this works because the Presenter has a getDisplay() method, and the WidgetDisplay has a asWidget() method returning the underlying widget. One would think that the asWidget() method is bad when unit- testing presenters, but you can simply return null in your WidgetDisplay mock. Actually, in our application, since we use SmartGwt, where the high- level component is a "Layout" instead of a "Widget" we created a LayoutPresenter and a LayoutDisplay (with a com.smartgwt.client.widgets.layout.Layout asLayout() method). We also have a WindowPresenter / WindowDisplay, where the WindowPresenter superclass handles some of the window logic (with a openWindow() method that calls bind() and then shows the window... and a closeWindow () that calls unbind() and then hides the window...). What do you think of this kind of architecture? I'm interested in other ideas and best practices as well. Regards, -Etienne --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "google-guice" 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-guice?hl=en -~----------~----~----~----~------~----~------~--~---
