How to expose events from child objects on the containing composite object? - gwt

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) {
...
}
});

Related

Adding Action Listeners at Netbeans

I have a Jbutton added on a frame automaticaly by netbeans.
I want to add this Actionlistener to a button.
public class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null,"hello");
}
My problem is that when I add this code
jButton2.addActionListener(MyActionListener);
in order to add the listener to the button I get a "non-static variable jButton2 cannot be referenced from a static context" message.
Since jButton2 is automatically created by netbeans as non static, how can overcome this problem and set the actionlistener as I want?
The error lies in that you are sending an object that has never been instantiated. When you add an action listener to any component you need to send an object that implements the ActionListener interface. Is like adding an instance of the listener to the object.
try this:
jButton2.addActionListener(new MyActionListener());
if you want to use an Anonymous implementation then you will do:
jButton2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//some code
}
});
An easy way to add a listener is just to right-click on the button from the design view, select
Events -> Action -> actionPerformed
and the code will be auto-generated for you
public void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// put what you want to happen, here
}

Can EventBus call a separated Presenter (not a nested Presenter) in GWTP?

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

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;
}
};

CellTree node selection reponding only the first time

I have a cell tree with a SingleSelectionModel. When I click on a node, it fires an certain action. My problem is that the action is fired only on the first click.
public class TreeModel implements TreeViewModel {
private SingleSelectionModel<Entity> selectionModel;
public TreeModel(){
initialize();
}
private void initialize(){
selectionModel = new SingleSelectionModel<Entity>();
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
//fire an action
}
});
}
public <T> NodeInfo<?> getNodeInfo(T value) {
...
}
The CellTree is called normaly
CellTree.Resources resource = GWT.create(TreeResources.class);
cellTree = new CellTree(new TreeModel(), null,resource);
panel.add(cellTree);
Any clue why it does that ?
Thanks
You mean clicking on the already-selected node? Well, in this case, you're not changing the selection, so there's no SelectionChangeEvent.
Maybe you're looking for the NoSelectionModel, or for something else than a SelectionModel (e.g. a Cell that responds to click events, or a CellPreviewHandler)

How can I disable EditTextCell and enable it with EditButton click in GWT?

I have a CellTable with EditTextCell, the EditButton is outside of the CellTable. The CellTable will be readonly until I click on the EditButton, the EditTextCell will enable for me to click and edit. Does anybody know how to do this? Thank you.
My custom class:
public class ButtonEnableEditTextCell extends EditTextCell {
private boolean click;
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event,
ValueUpdater<String> valueUpdater) {
if (click) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
public void setClick(Boolean click) {
this.click = click;
}
}
View with table:
CellTable<Employee> employeeTable;
#UiField
Button editButton;
ButtonEnableEditTextCell sequenceCell = new ButtonEnableEditTextCell();
#UiHandler("editButton")
public void editButtonClick(ClickEvent event) {
sequenceCell.setClick(true);
}
private void initTableColumns() {
Column<Employee, String> sequenceColumn = new Column<Employee, String>(sequenceCell) {
public String getValue(Employee employee) {
return employee.getSeqNumber().toString();
}
};
sequenceColumn.setFieldUpdater(new FieldUpdater<Employee, String>() {
public void update(int index, Employee employee, String value) {
employee.setSeqNumber(new Integer(value));
}
});
employeeTable.addColumn(sequenceColumn, "Sequence Number");
}
I am able to enable the sequenceColumn with this code. I wonder if there is better way to do it?
You will have to make your own cell. You can extend EditTextCell and override the onBrowserEvent function. If your button has been clicked and editing is enabled, then call super.onBrowserEvent. If editing is disabled, either do nothing or display an error message.
I had a similar need; except wanting to be able to change editability of each cell while running based on the row values. I tested out various combinations of overriding render, isEditing, resetFocus, and edit on EditTextCell (I didn't try the onBrowserEvent solution).
When I only overrode render (to show an HTML value if non-editable); I got errors resetting focus (as discussed here). This continued even if I overrode resetFocus. When I only override isEditing, the cell would flash to editing when clicked, and then flash back. What worked perfectly was overriding edit. I triggered based on adding a tag to the value passed in by Column.getValue, you can trigger however you like (ie some setting based on your edit mode), but disabling for a cell turned out to be as simple as:
private static class LockableEditTextCell extends EditTextCell {
#Override
protected void edit(Context context, Element parent, java.lang.String value) {
if (!value.startsWith(LOCKED_CELL_VALUE)) {
super.edit(context, parent, value);
}
}
}
Update: this had a bug where if you clicked an editable cell; then a locked cell; it would let you edit. You have to also override onBrowserEvent:
#Override
public void onBrowserEvent(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
if (!value.startsWith(IncentivesColumnConfigurator.LOCKED_CELL_VALUE)) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}