In project I use MVP pattern. I have 2 view and 2 corresponding presenters. From "Presenter2" i want to get selected value in "View1". What is the best way to do it? I know that better to use event bus. But so i must to create 2 events and 2 event handlers (1st event will rise when Presenter2 need selected value from View1, and it will be handled in Presenter1. 2nd event will rise in Presenter1 (like: new selectedValueEvent(value) to notificate Presenter2 about selected value. So Presenter2 will handle selectedValueEvent(value) and get value).
If the point when the presenter needs to get the selected value is when the user makes an action you won't get around using an event. (Altough, maybe both presenters could react to the same event so don't need to use two different ones?)
If it is known when the presenter needs to get the value (a defined step in a workflow), you could to it like this:
Keep a reference to the views in the ClientFactory:
public class ClientFactoryImpl implements ClientFactory {
private static final EventBus eventBus = new SimpleEventBus();
/* The views */
private static final SampleView sampleView = new SampleView();
....
public ClientFactoryImpl(){
eventBus.addHandler(ReleaseAddedEvent.type, sampleView);
....
}
// getter and setters
}
So in the Presenter you can get a reference to the view: SampleView view = MyEntryPoint.getClientFactory().getSampleView(); and then you can just call a method from the view which returns the selected value.
Related
In my application we are using
1. Screen are designed using UIBinder with a view class to bind
2. Presenter as controller and model.
3. We do have Base Presenter (abstract with few generic implementations)
4. We are having many such screens with all screen will have textbox in it and all screen presenter will extends Base Presenter.
-- My requirement is to fire and catch generic event when user changes any textbox in any screen.
-- I dont want to define event for each and every textboxes available in each screen.
-- I want to register all textbox on value change (ValueChangeHandler) event in generic.
Is there anyway we can achieve this.
As far as I understand, you want to add a value change handler to each and every textbox that appear in a set of screens without adding to each individual text box.
You can extend the textbox and add value change handler in the constructor and provide a hook to do any manipuation.
public class MyTextBox extends TextBox
{
public MyTextBox()
{
super();
addValueChangeHandler( new ValueChangeHandler<String>()
{
#Override
public void onValueChange( ValueChangeEvent<String> event )
{
// TODO Auto-generated method stub
}
} );
}
}
I am using the MVP design pattern and I have registered my presenter as a listener on the various buttons and other interactive elements on my view. And that works fine, I get notified whenever a user performs some action in the GUI.
However I don't the procedure for notifying the presenter when the model changes. I have data coming in from a server that gets processed in, for example, a User model, and changes the value of my User object.
How do you notify the presenter that the model has changed in Java?
Do I let my model hold a reference to the presenter and explicitly call something like presenter.userObjectHasBeenUpdated() in my model after I have altered the User object? Or is there a way of placing a listener on the User object that will automatically call presenter.userObjectHasBeenUpdated() when the User object gets modified?
I would say that you create a dedicated interface like Observer and you let your presenters implement it. Then each presenter can register themselves on the model objects to be notified whenever an object changes.
public interface Observer {
public void update(Object notification, Object source);
}
and in your model:
private List<Observer> observers = new ArrayList<Observer>();
public void addObserver(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
protected fireNotification(Object notification) {
for(Observer observer:observers) {
observer.update(notification, this);
}
}
Of course you can improve all this by adding Thread-safety, typing your notification etc...
If you are using Swing Components to show the data in view, you can use their respective models for notification. They also work as listeners but have finer levels of notifications depending on component. For example JTable can be notified of change in a row or change in whole table. See http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/table/AbstractTableModel.html#fireTableChanged(javax.swing.event.TableModelEvent) for an example.
I've been trying to make multiple Presenters "listen" to the same event but I which to make each event unique to the Presenter.
Ex.
I create 3 Composite widgets each in one different tab. They get all attached to the same event at binding. Let's call it the "NewPrescriptionEvent". If this event is fired, all my 3 composites will try to DO the job. I only want one of them to do it.
The only way I found to do this is by creating a temp event id (an integer inside the event) which I check for each widget which is trying to respond to the event.
Code snippet
private class OnNewPrescription implements NewPrescriptionHandler {
#Override
public void onNewPrescription(NewPrescriptionEvent event, int dataObjectId) {
if (getDataObject().getPatientId() == dataObjectId) {
...
}
}
}
During binding I do the usual:
eventBus.addHandler(NewPrescriptionEvent.TYPE, new OnNewPrescription());
The event:
public class NewPrescriptionEvent extends GwtEvent<NewPrescriptionHandler> {
public static final GwtEvent.Type<NewPrescriptionHandler> TYPE = new GwtEvent.Type<NewPrescriptionHandler>();
private int dataObjectId;
public NewPrescriptionEvent(int dataObjectId) {
this.dataObjectId = dataObjectId;
}
#Override
protected void dispatch(NewPrescriptionHandler handler) {
handler.onNewPrescription(this, dataObjectId);
}
#Override
public GwtEvent.Type<NewPrescriptionHandler> getAssociatedType() {
return TYPE;
}
}
I was thinking that the TYPE need to be different each time but still be the same event. Does anyone have a suggestion?
Thx.
Is it the case that you have an arbitrary number of instances of the same presenter and all are listening to the same event type? And each of your presenters 'controls' a different entity an therefore should only react on events coming from that entity? If that's the case the only solution I see is to parametrize the event as you've done.
Sounds like the EventBus probably isn't the best approach here; this is one of the main problems I've personally had with the EventBus: all events are global, and it's hard to differentiate between different events of a given type.
A good set of rules for asynchronous event handling without a shared EventBus is:
Communicate with child widgets via direct method calls.
Communicate with a parent widget via callbacks/handlers/listeners.
Avoid direct knowledge of sibling widgets (probably beside the point here)
So, the widget that contains the 3 tabs can attach callbacks to each tab that, when called, dispatches each event to its appropriate event handler (Presenters, in your case, I believe).
No global communication required, no knowledge of sources or destinations, only one event type, one reusable tab widget type, and the tab class stays simple. In principle, not too different from adding a ValueChangeHandler to a CheckBox (after all, one doesn't subscribe to check box events via the event bus, you just add a handler directly to the widget).
Rough sketch:
public class TabContainer implements IsWidget {
public TabContainer() {
tab1.addNewPrescriptionHandler(
new NewPrescriptionEventHandler() {
#Override
public void handleNewPrescriptionEvent(NewPrescriptionEvent event) {
handleTab1Event(event);
}
});
tab2.addNewPrescriptionHandler(
new NewPrescriptionEventHandler() {
#Override
public void handleNewPrescriptionEvent(NewPrescriptionEvent event) {
handleTab2Event(event);
}
});
...
}
}
And you might even be able to simplify that with some looping and pairing.
Going the other way, this container can also send events back the other way to your widgets from wherever else using the same principles.
Also, depending on what the Event class contains, you might not even need an Event class; you can define your callbacks and params however you want.
I think the title of the question is your answer.
You need different event types for each of the widgets.
You could try using addHandlerToSource(GwtEvent.Type<H> type, Object source, H handler) if you know the source to listen to.
Another possibility would be to extend EventBus to accept some kind of filter on registration.
I'm using the MVP pattern from my GWT application following the example given here http://code.google.com/webtoolkit/doc/latest/tutorial/mvp-architecture.html
I have a single MainPresenter and sub-presenter for each of the panels in the MainView. To show a new sub-presenter, I do something like this:
presenter = new PresenterA(new ViewA(), ....);
presenter.go(panel) // presenter clears the panel and itself to the panel
When PresenterA is created, it binds itself to events in the ViewA. My question is what is the proper way to switch to a new presenter? Right now, I'm simply creating a new presenter and attaching it to the same panel like this:
presenter = new PresenterB(new ViewB(), ....);
presenter.go(panel) // presenter clears the panel and itself to the panel
I have some doubts about this approach. First, am I causing a memory leak when I switch to the new presenter? I've lost both the field that references the old presenter and cleared it from the panel it was attached to. I suppose this means it should be garbage collected, but I'm not really sure. Secondly, what happens to the event bindings that the old presenter had? Will these bindings prevent the presenter from being garbage collected? Do I need unbind them first?
What is the correct way the handle the situation of switching presenters without memory leaks and binding to "dead" events.
I'd suggest that you take a look at the gwt-mvp and/or gwt-presenter libraries, which both take the same approach to this problem. Effectively, you create a base class for all presenters which maintains an internal list of all event registrations that the presenter has. When you then come to switch presenters, you call presenter.unbind() on the old presenter, which then removes all the event handlers you've created.
The base presenter class will look something like this:
public abstract class BasePresenter {
private List<HandlerRegistration> registrations = Lists.newLinkedList();
public void bind() {}
public void unbind() {
for(HandlerRegistration registration : registrations) {
registration.removeHandler();
}
registrations.clear();
}
protected void addHandler(HandlerRegistration registration) {
registrations.add(registration);
}
}
Then in the bind method of your presenter, you pass the HandlerRegistration object's into the addHandler() method:
bind() {
addHandler(foo.addBarHandler(...));
}
In Eclipse RCP, I am creating views for the Perspective using IPageLayout.addView(...)
But this way I don't have a reference to the view. Therefore I don't know how I can tell ViewA to update ViewB.
What's the best pattern to use here?
Besides what VonC has mentioned above, you can also use ISourceProviderListener if the changes you need are not triggered by selection.
Have ViewB implements ISourceProviderListener
Create an implementation of ISourceProvider and register it in the services
Have ViewA get the ISourceProvider and update it to trigger the changes in ViewB
Read the documentation on those interfaces along with IServiceLocator and ISourceProviderService to get better idea how it all plays out.
You can also see this Lars Vogel's tutorial which has some example how to use the ISourceProvider
You have the different communication paradigm summarize in the IBM article
To make a view capable of listening to selection changes, a view must implement the ISelectionListener interface and must register itself with the workbench page
Using the IAdaptable interface: A class that implements IAdaptable has the capability to dynamically return certain types of adapters that can then be used to retrieve further information.
property change listener paradigm
Regarding the first approach, the article details:
A smarter way to consume UI selections is to register the consumer views as listeners to specific view parts. As you can see in the example below, the view ID of the source view part is mentioned as a parameter during registering a selection listener.
getSite().getPage().addSelectionListener("SampleViewId",(ISelectionListener)this);
This approach will eliminate the redundant callbacks to the consumer view that would otherwise occur if that view were registered as a nonspecific listener.
The code snippet in Listing 2 shows the createPartControl() method of a view that creates a JFace TableViewer and adds it as a selection provider to the workbench site. This code enables any UI selection changes in the TableViewer to propagate to the page and finally to the interested consumer views.
Listing 2. Setting up a selection provider
public void createPartControl(Composite parent) {
// Set up a JFace Viewer
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
viewer.setSorter(new NameSorter());
viewer.setInput(getViewSite());
// ADD the JFace Viewer as a Selection Provider to the View site.
getSite().setSelectionProvider(viewer);
}
You will find a similar approach in the RCP tutorial for eclipse3.5 (update February, 4th 2010)
There are different ways for view and plugin communications: eventbroker, listener etc..
EvenBroker (e4) Implementation:
Use eventbroker to send message (string) between views and plugins.
Sender Side:
#Inject
private IEventBroker eventBroker;
private static final String STATUS ="status";
eventBroker.send(STATUS, "status test message..");
Receiver Side:
#Inject
private IEventBroker eventBroker;
private static final String STATUS ="status";
#Inject #Optional
public void getEvent(#UIEventTopic(STATUS) String message) {
... //call method
}