Ok, here is my problem. I have 2 presenters: FirstPresenter (ex: abc.com#first) & SecondPresenter (ex: abc.com#second). There is a button on SecondPresenter & when user clicks on that button then the FirstPresenter will popup a message.
So, here is what I did, I used eClipse to create an event name MyEvent, the eclipse generated a class MyEvent.java
On the SecondPresenter, I got:
private EventBus eventBus;
#Inject
public SecondPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy) {
super(eventBus, view, proxy);
this.eventBus=eventBus;
}
#Override
protected void onBind() {
super.onBind();
passMsgButton.addClickHandler(new ClickHandler(){
#Override
public void onClick(ClickEvent event) {
MyEvent myEvent =new MyEvent();
myEvent.setMsg("hello");
SecondPresenter.this.eventBus.fireEvent(myEvent);
}
});
}
On the FirstPresenter, I got:
private final MyHandler myHandler=new MyHandler(){
#Override
public void onMy(
MyEvent event) {
// TODO Auto-generated method stub
Window.alert(event.getMsg());
}};
#Override
protected void onBind() {
super.onBind();
registerHandler(getEventBus().addHandler(MyEvent.getType(), myHandler));
}
If the FirstPresenter is the nested presenter that is embedded inside the SecondPresenter then that above code works fine. But if they are 2 separated Presenters then the above code didn't work.
Why? I checked Google doc & they just say that EventBus can call a Presenter, they didn't say it must be the nested presenter so I assumed that EventBus can call any separated presenter.
I changed private EventBus to public EventBus, but it didn't help.
What am I missing?
EDIT:
I changed my code a bit, it works partially. I am not sure i'm doing right thing since it still has some issues.
Ok, on the FirstPresenter I implements MyHandler & Override onMy method
public class FirstPresenter extends
Presenter<FirstPresenter.MyView, FirstPresenter.MyProxy> implements MyHandler{
private final MyHandler myHandler=new MyHandler(){
#Override
public void onMy(
MyEvent event) {
// TODO Auto-generated method stub
Window.alert(event.getMsg());
}};
#ProxyEvent
#Override
public void onMy(MyEvent event) {
Window.alert(event.getMsg());
getView().getHtmlPanel().add(new Label("test"));
}
#Override
protected void onBind() {
super.onBind();
registerHandler(getEventBus().addHandler(MyEvent.getType(), myHandler));
}
}
When I click the button on SecondPresenter (a page on a 2nd tab of Webbrowser) then I saw the message popup on the 2nd tab (i.e. the browser didn't make the 1st tab on focus), but when I click on the FirstPresenter (a page on 1st tab) I didn't see the Label("test")?
Why it can call the Window.alert but didn't add the Label onto the FirstPresenter?
Also, how can I get the Browser to set focus on the FirstPresenter (i.e. show the the first tab)?
Am I missing something?
The problem is that if you have two top level presenters only one is active at any time.
I guess that the onBind() method of the FirstPresenter hasn't been called and thus the handler hasn't been attached to the Event on the EventBus.
It works with nested presenters because there both Presenters are "active" at the same time.
You have to rely on ProxyEvent to "wake up the FirstPresenter
Related
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!
I've followed Dani's GWTP Course but using TabLayoutPanel with presenters isn't covered.
I have a TabLayoutPanel with 3 tabs (each with a VerticalPanel on it). I've used #ProxyCodeSplit so that the code for each tab is loaded independently.
If in Eclipse, in GWT's Designer I add a handler for OnBeforeSelection then code is auto-added into my View. The View can then load up the appropriate presenter.
That doesn't feel like the right place for the code - but is it?
How are you handing different tabs within TabLayoutPanel and code splitting?
I think I've got this figured out.
In your presenter with the TabLayoutPanel (let's call it MainPresenter):
#ContentSlot public static final Type<RevealContentHandler<?>> SLOT_first = new Type<RevealContentHandler<?>>();
#ContentSlot public static final Type<RevealContentHandler<?>> SLOT_second = new Type<RevealContentHandler<?>>();
public interface MyView extends View {
public void setMainPresenter(MainPresenter presenter);
public TabLayoutPanel getTeamsPanel();
}
#Inject PlaceManager placeMananger;
#Inject FirstPresenter firstPresenter;
#Inject SecondPresenter secondPresenter;
#ProxyCodeSplit
public interface MyProxy extends Proxy<MainPresenter> {
}
#Inject
public MainPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy) {
super(eventBus, view, proxy);
view.setMainPresenter(this);
}
#Override
protected void revealInParent() {
RevealRootContentEvent.fire(this, this);
}
public void setTabContents(Integer tab) {
if (tab == 0) {
placeMananger.revealPlace(new PlaceRequest("first"));
} else if (tab == 1) {
placeMananger.revealPlace(new PlaceRequest("second"));
}
Then in your MainView implement the method setMainPresenter() to store a reference locally. Implement the usual setInSlot() and then add this tab handler:
#UiHandler("mainTabs")
void onMainTabsBeforeSelection(BeforeSelectionEvent<Integer> event) {
mainPresenter.setTabContents(event.getItem());
}
The handler will call MainPresenter each time the user changes tabs. setTabContents() will then call revealInParent() for the appropriate "tab" Presenter.
I am constructiong an webapp with Google Web Toolkit using GWT-Platform and GWT-Bootstrap frameworks. Mostly it has been almost flawless until I tried to implement a popup. These frameworks' undestanding of popups seems to be quite different.
GWT-Platform expects a popup widget itself to be an instance of com.google.gwt.user.client.ui.PopupPanel when using the GWTP's RevealRootPopupContentEvent.fire(source, content) or a presenter's addToPopupSlot(child) method.
GWT-Bootstrap's Modal is used like any other widget that is added to the underlying panel but my goal is it to have a separate presenter and view and to possibly fetch it asynchrously with AsyncProvider.
I have tried to make it as a PresenterWidget and using addToSlot(slot, content) to reveal it but it doesn't look quite right. Not all of the styles are applied this way and the close icon (×), doesn't work for example.
I think I am not the first one trying to do something like that so maybe someone has figured out a proper way to make it work.
Thanks!
You have to create a view:
public class MyPopupView extends PopupViewImpl implements MyView {
protected Widget widget;
public interface MyPopupViewUiBinder extends
UiBinder<Widget, MyPopupView> {
}
#UiField(provided = true)
Modal dialogBox;
private MyPresenter presenter;
#Inject
public MyPopupView(final MyPopupViewUiBinder uiBinder,
final EventBus eventBus) {
super(eventBus);
setUpDialog(); // Provides UiField => Before initWidgets
initWidget(uiBinder.createAndBindUi(this));
}
// DialogBox must be overridden to let the presenter handle changes onUnload
private void setUpDialog() {
dialogBox = new Modal() {
#Override
protected void onUnload() {
MyPopupView.this.hide();
}
};
dialogBox.setTitle("Some title");
}
#Override
public void setPresenter(final MyPresenter presenter) {
this.presenter = presenter;
}
#Override
public final void hide() {
dialogBox.hide();
presenter.hide();
}
#Override
public void setAutoHideOnNavigationEventEnabled(final boolean autoHide) {
// TODO Auto-generated method stub
}
#Override
public void setCloseHandler(
final PopupViewCloseHandler popupViewCloseHandler) {
// TODO Auto-generated method stub
}
#Override
public void setPosition(final int left, final int top) {
// TODO Auto-generated method stub
}
#Override
public void show() {
dialogBox.show();
}
#Override
public void center() {
dialogBox.show();
}
#Override
public Widget asWidget() {
return widget;
}
protected final void initWidget(final Widget widget) {
this.widget = widget;
}
}
And a UIBinder file:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:b='urn:import:com.github.gwtbootstrap.client.ui'>
<b:Modal title="Some Title" ui:field="dialogBox">
<!-- Your content -->
</b:Modal>
</ui:UiBinder>
Your gwtp popup presenter has a view that extends PopUpViewImpl which implements PopupView, and uses a lot of the methods of that interface for displaying the popup (asPopupPanel(), show(), center(), etc).
I'm just starting to get to know gwt-bootstrap (looks great +caalos0), but it seems that Modal doesn't implement PopupView, and therefore cannot be passed to addToPopupSlot in a way it would be displayed automatically by gwtp.
as for the addToSlot() issue, are you using RootLayoutPanel or RootPanel?
it could be the reason for addToSlot not working properly, since the gwt-bootstrap Modal widget is attached to the RootPanel on initialization, this can cause weird layout behavior along with an application using RootLayoutPanel as base.
I would try to extend the Modal component, let it implement PopUpView, add it as a field on the PopUpViewImpl attached to your popup presenter, and override the PopUpViewImpl asPopupPanel() function to return the new extended Modal.
Based on the answer by #dominik I did some improvements, see my Gist. It contains some abstract base classes that can be used for any Modal/PopupView implementation. It's a bit more complex but also cleaner because we don't pass the whole Presenter to the View. The interface for the View to interact with the Presenter when the modal is closed is HasModalUnbind.
You would use these classes as follows. Example presenter:
public class ErrorModalPresenter extends ModalPopupPresenter<ErrorModalPresenter.MyView> {
public interface MyView extends ModalPopupView {
DivElement getErrorMessage();
}
private final ErrorEvent error;
#Inject
public ErrorModalPresenter(final EventBus eventBus,
final MyView view,
#Assisted final ErrorEvent error) {
super(eventBus, view);
this.error = error;
}
#Override
public void unbindModal() {
ErrorDismissEvent.fire(this, this);
}
#Override
protected void onBind() {
super.onBind();
//noinspection ThrowableResultOfMethodCallIgnored
getView().getErrorMessage().setInnerText(error.getCause().getMessage());
}
}
Example view:
public class ErrorModalView extends ModalPopupViewImpl implements ErrorModalPresenter.MyView {
#UiField(provided = true)
Modal errorModal;
#UiField
DivElement errorMessage;
interface Binder extends UiBinder<Widget, ErrorModalView> {}
#Inject
public ErrorModalView(final EventBus eventBus,
final Binder uiBinder) {
super(eventBus);
errorModal = initModal();
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public DivElement getErrorMessage() {
return errorMessage;
}
}
And the UiBinder XML just for the record:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:b='urn:import:com.github.gwtbootstrap.client.ui'>
<b:Modal ui:field='errorModal' title='Error'>
<g:HTML>
<div ui:field='errorMessage'/>
</g:HTML>
<b:ModalFooter>
<b:Button text='Close' dismiss='MODAL'/>
</b:ModalFooter>
</b:Modal>
</ui:UiBinder>
In unbindModal() of ErrorModalPresenter I fire an event which is caught by the parent presenter of ErrorModalPresenter. There the modal presenter is removed from a container and then unbind() is called on the presenter. Of course any other solution is possible in unbindModal().
The base classes assume that modals are one-shot modals that will be removed once they're hidden. This behaviour can be changed in initModal() of ModalPopupViewImpl.
I believe you will have to made some Glue Code to made it works.
I never used GWT-Platform popups, so I dont know exactly how, but I believe you will have to made a new Class extending PopupPresenter, and made what's needed to made it work.
Also, I was thinking about GWT-Platform days ago... and I'm pretty sure that when first release of GWT-Platform is out, I'll create a new project to made these necessary glue codes.
If you need any help with this, please contact me.
Thanks, sorry about the poor gwt-platform support.
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;
}
};
I have a composite widget that contains many buttons (eg. Button1, Button2, etc). I can't figure how to expose button click events on the composite widget. I'm trying to avoid creating custom events such as Button1ClickEvent and Button2ClickEvent and instead reuse the existing GWT ClickEvent for both of them. The following code snippet can give an idea what I'm trying to do:
public class WidgetWithTwoButtons extends Composite {
...
#UiField Button button1;
#UiField Button button2;
#UiHandler("button1")
void onButton1Click(ClickEvent event) {
// TODO fire click event on Button1ClickHandler
}
public HandlerRegistration addButton1ClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
#UiHandler("button2")
void onButton1Click(ClickEvent event) {
// TODO fire click event on Button2ClickHandler
}
public HandlerRegistration addButton2ClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
I think this is not the best practice. I'd appreciate if you give pointers to recommended solutions/examples to this problem in your answers. Thanks!
I started thinking about your problem and first noticed instead of using addDomHandler you can change your current addButton1ClickHandler(ClickHandler handler) simply to :
public HandlerRegistration addButton1ClickHandler(ClickHandler handler) {
return button1.addClickHandler(handler);
}
If you had only one button, you could expose it by making your Composite implement HasClickHandlers and implement the below method to pass it through to your button:
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return button1.addClickHandler(handler);
}
But since you have many buttons, you need a method that takes the button you want to add your clickHandler to as a parameter such as :
public HandlerRegistration addClickHandlerToButton(ClickHandler handler, Button target){
return target.addClickHandler(handler);
}
The problem with this approach is, to use this you need to have a reference to your buttons which means you need to define getter methods for your buttons in your composite such as public Button getButton1() . When you expose your buttons like this, the problem is one does not need a passthrough method on the composite anymore since he/she can directly access buttons anyway making the above approach that takes a target button as parameter obsolete. Worst part is he/she can even change the styles and even detach those buttons.
To solve this you can expose your buttons over the HasClickHandlers interface.
So IMHO this is how i beleive it should be done:
public class ComplexComposite extends Composite {
private Button button1 = new Button("btn1");
private Button button2 = new Button("btn2");
public ComplexComposite(){
HorizontalPanel panel = new HorizontalPanel();
panel.add(button1);
panel.add(button2);
initWidget(panel);
}
public HasClickHandlers getButton1(){
return button1;
}
public HasClickHandlers getButton2(){
return button2;
}
}
With this approach you expose your buttons only over the desired interface and one can add
click handlers such as :
ComplexComposite composite = new ComplexComposite();
composite.getButton1().addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
...
}
});