0

I am using mvp4g in my gwt project. For one of my presenters I am using option multiple=true and I am creating and binding presenters in that way:

ObjectPresenter mainObject = eventBus.addHandler(ObjectPresenter.class, false);
mainObject.setId(id);
mainObject.bind();
view.addWidget(mainObject.getView().asWidget());

ObjectPresenter extends LazyPresenter.

When I am calling first event from the eventBus that is caught by ObjectPresenter, method bind() of the LazyPresenter is called again.

bind method has inside tree other methods: createPresenter(); view.createView(); bindView();. In the bindView method of the ObjectPresenter I am modifing my view by adding next widgets. Because the method is called twice (once directly by me, and once by framework) some widgets are duplicated.

I've debugged the code and I found that this part of code from BaseEventHandler is called when the event from the eventBus is called:

public final boolean isActivated( boolean passive, String eventName, Object... parameters ) {
    boolean activated = this.activated && pass( eventName, parameters );
    if ( activated ) {
        if ( passive ) {
            return binded;
        } else {
            onBeforeEvent();
            if ( !binded ) {
                bind();
                binded = true;
            }
        }
    }
    return activated;
}

After calling bind directly (by mainObject.bind()) binded property in the BaseEventHandler is not set to true, so bind method is called again when first event is called.

I can set binded variable from the BaseEventHandler to true in the ObjectPresenter when the method bind (called directly) is finished, but I am not sure if it is proper approach...

Could you please give me a hint how to deal with this issue?

Thanks for your help.

kpater87
  • 1,190
  • 12
  • 31

2 Answers2

0

First of all the question, do you really need the multiple=true-featrue. This feature is designed to use sereval instances of the same presenter class at one time. If this is not your case, do not use it, because you'll have to write a lot of code, which is normally generated by the mvp4g-framework.

Also, you should never call the bind-method directly.

If you need the multiple-feature, you can create a handler (which extends BaseHandler-class). This handler should manage your presenter instances. The coding should contain a list of your presenter instances:

  private List<EventHandlerInterface<MyEventBus>> presenters = new ArrayList<EventHandlerInterface<MyEventBus>>();

To show a new instance or even to bring an existing instance to front, the handler should listen to an event (here: eventBus.showObject([someNumber]);:

public void onShowObject(long id) {
  // check if a presenter for that Id already exists
  if (presenters.size() > 0) {
    for (int i = 0; i < presenters.size(); i++) {
      ObjectPresenter presenter = (ObjectPresenter) presenters.get(i);
      if (presenter.getId() == idNr) {
        eventBus.setCurrentObject(presenter.getView().asWidget());
        return;
      }
    }
  }
  // no presenter found, create a new one and add the presetner instance to the list 
  ObjectPresenter mainObject = eventBus.addHandler(ObjectPresenter.class);
  mainObject.setId(id);
  presenters.add(mainObject);
  eventBus.setCurrentObject(mainObject.getView().asWidget());
}

}

The event setCurrentObject(Widget widget) will make your view visible. So your shell must also be implemented as a presenter/view-combination, wich have to listen for the setCurrentObject(Widget widget)-event. Using this code, you don't need to call the bind-method.

I'll use this code in several project and it works really nice. Also, it is quite easy to use history managment with this code.

Hope that helps.

El Hoss
  • 3,767
  • 2
  • 18
  • 24
  • I am using addHandler(ObjectPresenter.class) as you said. After creating the presenter I would like to add his view to the main panel using `view.addWidget(mainObject.getView().asWidget());`. To do that I have to bind whole presenter using `bind` method or only the view using `bindView` (yes, I am using Lazy presenter). The problem is when I am calling event from event bus that is caught by `ObjectPresenter`. The `BaseEventHandler` is calling `bind` method again, because the `bineded` variable is not set to `true`. I've also modified the question to make it more clear... – kpater87 Jun 21 '14 at 13:57
  • Sorry for late answer. I've just tested your proposal and it doesn't work as I expected. I need to set presenter fields before binding the view, therefore I am calling `eventBus.addHandler(ObjectPresenter.class, false);` (bind parameter set to false). Then after setting fields I would like to bind the presenter so I am calling `mainObject.bind();` but it doesn't set the `binded` field to `true`, so when the first event caught by ObjectPresenter is called the presenter is binded second time. – kpater87 Jul 22 '14 at 15:43
  • Please see also my answer. – kpater87 Jul 22 '14 at 17:31
  • You did not answer my question. Do you really need more then one instance of the ObjectPresenter class? If not, don't use the `multiple=true`-feature. Second let the framework create your view & presenter. Use events to present the ObjectView and to add it to your shell. In this case everything will work as expected. – El Hoss Jul 23 '14 at 07:05
  • I have such case. On my page I am displaying many panels for many objects (one panel per object). Each panel has the same bahavior, so I've implemented one presenter with option multiple=true. About second part. Yes framework will create everything for me, but it will bind the view before the presenter fields will be set. I would like to delay binding to the moment when all fields will be set. – kpater87 Jul 23 '14 at 07:32
0

I haven't read documentation carefully enough! I was using method eventBus.addHandler(ObjectPresenter.class, false) (with bind parameter), which has following java doc.

     /**
     * Create a new instance of the handler, bind it only if this option is set to true and add it
     * to event bus. If you decide not to bind the handler at creation, you will have either make
     * sure the handler is displayed only after it handles its first method (otherwise the view is
     * not binded so it seems inactive) or call manualy the bind method.<br/>
     * <br/>
     * When binding the handler, you have to call the isActivated method. This method will be called
     * with eventName and parameters set to null.
     * 
     * @param <T>
     *            type of the handler created
     * @param handlerClass
     *            class of the handler to create
     * @param bind
     *            if true, bind the handler at creation, otherwise do nothing.
     * @return new instance of the handler created
     * 
     * @throws Mvp4gException
     *             thrown if the instance of the handler can not be created by the event bus
     */
    <E extends EventBus, T extends EventHandlerInterface<E>> T addHandler( Class<T> handlerClass, boolean bind ) throws Mvp4gException;

The part I missed is When binding the handler, you have to call the isActivated method. This method will be called with eventName and parameters set to null. and that was my problem - I didn't do this!

kpater87
  • 1,190
  • 12
  • 31
  • Don't change the internal flags of the framework. You will get a lot of trouble. – El Hoss Jul 23 '14 at 07:07
  • So why this method is provided by the framework, if I shouldn't use it? – kpater87 Jul 23 '14 at 07:35
  • For me this solution works properly and it is consistent with documentation, so I am accepting it. Nevertheless, thank you for all the help in understanding the issue. – kpater87 Jul 23 '14 at 07:53
  • There are a lot of methods (and the bind-method is another one) that are needed and used by the framework and not for public use. The framework has a defined idea which method when to call and which flag should be set. Calling this method directly might produced impacts with future versions. – El Hoss Jul 23 '14 at 08:11