Activity handlers don't get removed - gwt

I'm trying to get up to speed on using GWT Activities and Places. I'm testing with some source code originally found on this good blog post.
I'm finding the Handlers that get added during bind() never seem to removed. My little understanding of the Activity javadoc had me thinking they should get automagically removed by the time the Activity's onStop() method is invoked.
All event handlers it registered will have been removed before this
method is called.
But each time I click a button the corresponding handler is called n+1 times.
What am I missing? Please let me know if there is more info I can provide.
Here's a relevant snippet from the code:
public class ContactsActivity extends AbstractActivity {
private List<ContactDetails> contactDetails;
private final ContactsServiceAsync rpcService;
private final EventBus eventBus;
private final IContactsViewDisplay display;
private PlaceController placeController;
public interface IContactsViewDisplay {
HasClickHandlers getAddButton();
HasClickHandlers getDeleteButton();
HasClickHandlers getList();
void setData(List<String> data);
int getClickedRow(ClickEvent event);
List<Integer> getSelectedRows();
Widget asWidget();
}
public ContactsActivity(ClientFactory factory) {
GWT.log("ContactActivity: constructor");
this.rpcService = factory.getContactServiceRPC();
this.eventBus = factory.getEventBus();
this.display = factory.getContactsView();
this.placeController = factory.getPlaceController();
}
#Override
public void start(AcceptsOneWidget container, EventBus eventBus) {
GWT.log("ContactActivity: start()");
bind();
container.setWidget(display.asWidget());
fetchContactDetails();
}
public void bind() {
GWT.log("ContactActivity: bind()");
display.getAddButton().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
GWT.log("Add button clicked");
ContactsActivity.this.placeController.goTo(new NewContactPlace(""));
}
});
display.getDeleteButton().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
GWT.log("ContactActivity: Delete button clicked");
deleteSelectedContacts();
}
});
display.getList().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
GWT.log("ContactActivity: List clicked");
int selectedRow = display.getClickedRow(event);
if (selectedRow >= 0) {
String id = contactDetails.get(selectedRow).getId();
ContactsActivity.this.placeController.goTo(new EditContactPlace(id));
}
}
});
}

Events registered via. the EventBus passed to AbstractActivity#start() will be unregistered by the time onStop() is called. The event handlers registered in the above bind() method, however, are not registered via the EventBus and are not visible to the abstract base class. You need to unregister them yourself:
public class ContactsActivity extends AbstractActivity {
private List<HandlerRegistration> registrations = new ArrayList();
private void bind() {
registrations.add(display.getAddButton().
addClickHandler(new ClickHandler() { ... }));
registrations.add(display.getDeleteButton().
addClickHandler(new ClickHandler() { ... }));
registrations.add(display.getList().
addClickHandler(new ClickHandler() { ... }));
}
#Override
public void onStop() {
for (HandlerRegistration registration : registrations) {
registration.removeHandler();
}
registrations.clear();
}
}

I found it best to handle registration in the view - make it responsible for only keeping one click hander active for each button.
Instead of:
class View {
Button commitButton;
public HasClickHandlers getCommit () {return commitButton;}
}
..and link to this in the Activity:
view.getCommit.addClickHandler(new Clickhandler()...
Do this in the View:
class View {
private Button commitButton;
private HandlerRegistration commitRegistration = null;
public void setCommitHandler (ClickHandler c) {
commitRegistraion != null ? commitRegistration.removeRegistration ();
commitRegistration = commitButton.addClickHandler (c);
}
}
And the Activity:
view.setCommitHandler (new ClickHandler () ...
Hope that helps.

Related

Setting a WorkbenchWindowControlContribution as a SelectionProvider

I added a control in the toolbar which extends WorkbenchWindowControlContribution and implements ISelectionProvider. This control contains a Combo and when I change the selection I want to notify another view. Because it's not a ViewPart I can't call getSite().setSelectionProvider directly. I tried to set it like this:
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart().getSite().setSelectionProvider(MyControl.this);
});
If I add this to the protected Control createControl(Composite parent) method the getActivePart would always be null. I also tried registering it when the Combo's value changes for the first time, but that way my other View wasn't notified about the change event.
Here is the ISelectionProvider methods:
#Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
listeners.add(listener);
}
#Override
public ISelection getSelection() {
return new StructuredSelection(comboBox.getText());
}
#Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
listeners.remove(listener);
}
#Override
public void setSelection(ISelection selection) {
for (ISelectionChangedListener listener : listeners) {
listener.selectionChanged(new SelectionChangedEvent(this, selection));
}
}
The View that should be getting notified implements ISelectionListener. I register it in the public void createPartControl(Composite parent) with getSite().getPage().addSelectionListener(this);
And here is the override of the SelectionChanged too:
#Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (part instanceof MyControl) {
String selectedProtocol = ((StructuredSelection) selection).getFirstElement().toString();
}
}
Can it be done?
Thanks!

How to use same servlet for RPC and upload File in GWT.

i created a web application where i have to use fileUpload.
i have to send the file and their properties to server . For sending a file i used FormPanel and for properties i used RPC .
public void onModuleLoad() {
final FileServiceEndPoint serviceEndPoint = new FileServiceEndPoint();
new AddDocument();
Button b = new Button("addDocument");
b.addClickHandler(new ClickHandler() {
private Map<String, String> docProperty;
public void onClick(ClickEvent event) {
docProperty =getProperties();
AsyncCallback<String> callback = new AsyncCallback<String>() {
public void onSuccess(String result) {
System.out.println("he ha" +result);
}
public void onFailure(Throwable caught) {
}
};
serviceEndPoint.uploadAttachement(docProperty, callback);
}
});
RootPanel.get().add(b);
}
this new AddDocument(); contains code for uploading a file (formPanel code)
private FormPanel getFormPanel() {
if (uploadForm == null) {
uploadForm = new FormPanel();
uploadForm.setAction(GWT.getHostPageBaseURL() +"TestUploadFileServlet");
uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
uploadForm.setMethod(FormPanel.METHOD_POST);
uploadForm.setWidget(getFileUpload());
System.out.println(GWT.getHostPageBaseURL() +"TestUploadFileServlet");
uploadForm.addFormHandler(new FormHandler() {
public void onSubmitComplete(FormSubmitCompleteEvent event) {
AddDocument.this.hide(true);
}
public void onSubmit(FormSubmitEvent event) {
}
});
}
return uploadForm;
}
private Button getAddButton() {
if (addButton == null) {
addButton = new Button("ADD");
addButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
uploadForm.submit();
}
});
addButton.setText("Add");
}
Interface is created for Sending property.
EndPoints:
public class FileServiceEndPoint implements FileServiceAsync{
FileServiceAsync service = (FileServiceAsync)GWT.create(FileService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) service;
public FileServiceEndPoint() {
endpoint.setServiceEntryPoint(GWT.getHostPageBaseURL() + “TestUploadFileServlet”);
}
public void uploadAttachement(Map docProperty,
AsyncCallback callback) {
service.uploadAttachement(docProperty, callback);
}
}
On Server:
public class FileUploadImpl extends RemoteServiceServlet implements FileService {
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(FileUploadImpl.class
.getName());
String a;
protected void service(final HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
a=”5″;
System.out.println(“ServletWorking Fine “);
}
public String uploadAttachement(Map docProperty) {
// TODO Auto-generated method stub
return “Checked”;
}
}
When I debug formPanel.submit : the debugger goes in Server and print ServletWorking Fine(this is perfect)
and when i debug the addProperties button it goes to server and print ServletWorking Fine. but It should not go in service method.
the debugger should go in UploadAttachement.
Plz tell how to pass hashMap using same servlet.

GWT testing with mockito

Simple Question:
Verification (1) passes.
Verification (2) does not. Why? How to fix it?
Test
#Test
public void test() {
System.out.println("test");
EventBus eb = mock(EventBus.class);
MyWidget.View v = mock(MyWidget.View.class);
GreetingServiceAsync s = mock(GreetingServiceAsync.class);
HasClickHandlers button = mock(HasClickHandlers.class);
when(v.getButton()).thenReturn(button);
new MyWidget(eb, v, s);
button.fireEvent(mock(ClickEvent.class));
verify(button).addClickHandler(any(ClickHandler.class)); (1)
verify(v).alert(anyString()); (2)
}
Widget
#Inject
public MyWidget(EventBus eventBus, View view, GreetingServiceAsync service){
this.view = view;
this.service = service;
bindView();
bindEventBus();
}
private void bindView(){
view.getButton().addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
view.alert("test");
}
});
}
because button is a mock, so calling fireEvent on it doesn't actually fire the event; and onClick is never called on the view.
Because Button was mocked out and there is no implementation telling it what to do when fireEvent is called. See the line:
HasClickHandlers button = mock(HasClickHandlers.class);
...
button.fireEvent(mock(ClickEvent.class));
As David Wallace said, you are mocking the button. It does lose all its abilities.
you could fix this by making a ArgumentCatptor
ArgumentCaptor<ClickHandler> captor = ArgumentCaptor.forClass(ClickHandler.class);
Then manually fire the function of the event by using:
captor.getValue().onClick(null);
This will fake the call that should have been made by the button.
If your class only has one button or one catcher for a specific event you can make it extend the ClickHandler class. Then you can just call the onClick of your class.
That is what I did:
public class ClickableElement implements HasClickHandlers{
ClickHandler ch;
#Override
public void fireEvent(GwtEvent<?> event) {
ch.onClick((ClickEvent) event);
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
this.ch = handler;
return null;
}
};

PlaceRequest with parameters to Popup Presenter

I trying to pass a parameter in the placerequest to a presenter that will be a popup, but, the i receive empty parameters in the popup presenter.. am i forgot anything?
AddProjetoPresenter
public class AddProjetoPresenter extends Presenter<AddProjetoPresenter.AddProjetoView, AddProjetoPresenter.AddProjetoProxy>
{
#ProxyCodeSplit
#NameToken(NameTokens.addproj)
public interface AddProjetoProxy extends ProxyPlace<AddProjetoPresenter>
{
}
public interface AddProjetoView extends View
{
HasValue<String> getNome();
HasValue<Date> getDtInicio();
HasValue<Date> getDtFim();
HasClickHandlers getAddRequisitos();
HasClickHandlers getAddStakeholders();
HasClickHandlers getBtCancelar();
HasClickHandlers getBtSalvar();
}
private final DispatchAsync dispatch;
private final PlaceManager placeManager;
private Projeto projeto;
#Inject
public AddProjetoPresenter(final EventBus eventBus, final AddProjetoView view, final AddProjetoProxy proxy, final DispatchAsync dispatch,
final PlaceManager placeManager)
{
super(eventBus, view, proxy);
this.dispatch = dispatch;
this.placeManager = placeManager;
}
#Override
protected void revealInParent()
{
RevealContentEvent.fire(this, MainPresenter.TYPE_SetMainContent, this);
}
#Override
protected void onBind()
{
super.onBind();
getView().getBtSalvar().addClickHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
}
});
getView().getAddRequisitos().addClickHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
PlaceRequest pr = new PlaceRequest(NameTokens.addreq);
pr.with("oi", "oiiiii"); // HERE
placeManager.revealPlace(pr, false);
}
});
}
}
AddRequisitoPresenter
public class AddRequisitoPresenter extends Presenter<AddRequisitoPresenter.AddRequisitoView, AddRequisitoPresenter.AddRequisitoProxy>
{
#ProxyCodeSplit
#NameToken(NameTokens.addreq)
public interface AddRequisitoProxy extends ProxyPlace<AddRequisitoPresenter>
{
}
public interface AddRequisitoView extends PopupView
{
DialogBox getDialog();
}
private final DispatchAsync dispatcher;
private Projeto projeto;
#Inject
public AddRequisitoPresenter(final EventBus eventBus, final AddRequisitoView view, final AddRequisitoProxy proxy, final DispatchAsync dispatcher)
{
super(eventBus, view, proxy);
this.dispatcher = dispatcher;
}
#Override
public void prepareFromRequest(PlaceRequest request)
{
super.prepareFromRequest(request);
getView().getDialog().setText(request.getParameterNames().size() + ""); //SIZE IS ZERO!!
}
#Override
protected void onBind()
{
super.onBind();
}
#Override
protected void revealInParent()
{
RevealRootPopupContentEvent.fire(this, this);
}
}
I think ai doing something wrong...
thanks in advance.
From what I understood in the wiki, a popup can't be a place, and it needs a parent presenter.
I see two obvious problems here :
Your second presenter (the popup) should implement PresenterWidget, not Presenter
You can't display a popup by calling placeManager.revealPlace(), because a popup is not a place. Instead, you have to apply one of the two methods explained in the wiki (addToPopupSlot() or RevealRootPopupContentEvent.fire(), both called from the parent).

Register KeyDownHandler on GWT VerticalPanel

I have a gwt VerticalPanel class that i need to handel KeyDown events for it.
the method i used to implement keyboard handler in my class is:
i add :
this.sinkEvents(Event.ONKEYDOWN);
to constructor
then i override method onBrowserEvent() to handle key down event.
#Override
public void onBrowserEvent(Event event) {
// TODO Auto-generated method stub
super.onBrowserEvent(event);
int type = DOM.eventGetType(event);
switch (type) {
case Event.ONKEYDOWN:
//call method to handle this keydown event
onKeyDownEvent(event);
break;
default:
return;
}
}
however this method doesn’t work for this VerticalPanel class.no KeyDown Event is fired when i press a key!
there are specific gwt widgets that support KeyDownHandler like Button etc..VerticalPanel is not one of them..so we need a work around to register a KeyDownHandler on a class extending VerticalPanel.
can you suggest an idea or hint?
thanks
You could create a Composite that wrappes a FocusPanel and a VerticalPanel. This way you can catch all key events provided the FocusPanel is focused. Simply delegate the needed methods to the panels:
public void onModuleLoad() {
ExtendedVerticalPanel panel = new ExtendedVerticalPanel();
panel.add(new Label("some content"));
panel.addKeyDownHandler(new KeyDownHandler() {
#Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
Window.alert("enter hit");
}
}
});
RootPanel.get().add(panel);
}
private class ExtendedVerticalPanel extends Composite implements HasWidgets, HasAllKeyHandlers {
private VerticalPanel fVerticalPanel;
private FocusPanel fFocusPanel;
public ExtendedVerticalPanel() {
fVerticalPanel = new VerticalPanel();
fFocusPanel = new FocusPanel();
fFocusPanel.setWidget(fVerticalPanel);
initWidget(fFocusPanel);
}
#Override
public void add(Widget w) {
fVerticalPanel.add(w);
}
#Override
public void clear() {
fVerticalPanel.clear();
}
#Override
public Iterator<Widget> iterator() {
return fVerticalPanel.iterator();
}
#Override
public boolean remove(Widget w) {
return fVerticalPanel.remove(w);
}
#Override
public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
return fFocusPanel.addKeyUpHandler(handler);
}
#Override
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return fFocusPanel.addKeyDownHandler(handler);
}
#Override
public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
return fFocusPanel.addKeyPressHandler(handler);
}
}
UPDATE
Your question on how to prevent the browser from scrolling when the arrow keys are pressed. Here a small example that works for me:
public void onModuleLoad() {
ExtendedVerticalPanel panel = new ExtendedVerticalPanel();
// make panel reeeeaally big
panel.setHeight("3000px");
panel.add(new TextBox());
panel.addKeyDownHandler(new KeyDownHandler() {
#Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_DOWN) {
Window.alert("down hit");
event.preventDefault();
}
}
});
RootPanel.get().add(panel);
}
Add the handlers you need and call preventDefault() on the events the browser must not take care of.