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)
Related
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 created a sample view in eclipse using the following code.i want the view to be
automatically refereshed.the part of code in quotes "" gives refresh option but it is done
manually.can anyone help me know how it can be done automatically
public class SampleView extends ViewPart {
public static final String ID = "tab.views.SampleView";
private TableViewer viewer;
class ViewContentProvider implements IStructuredContentProvider {
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
public void dispose() {
}
public Object[] getElements(Object parent) {
return new String[] { "Status of your hudson build is: " +hudson.d};
}
}
class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
public String getColumnText(Object obj, int index) {
return getText(obj);
}
public Image getColumnImage(Object obj, int index) {
return getImage(obj);
}
public Image getImage(Object obj) {
return PlatformUI.getWorkbench().
getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD);
}
}
public SampleView() {
}
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
viewer.setInput(getViewSite());
PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), "Tab.viewer");
hookContextMenu();
}
" private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu");
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
Action refresh =new Action() {
public void run() {
// initialize();
viewer.refresh();
}
};
refresh.setText("Refresh");
menuMgr.add(refresh);
}"
public void setFocus() {
viewer.getControl().setFocus();
}
}
It is only possible to refresh the tree contents automatically, if you fill it using JFace Data Binding, that would not work with remote build results.
I recommend either using a model with notification support: when the model changes, its listeners are notified. Then your view could listen for these notifications and refresh itself.
If for some reason this is not possible, you have to poll your models manually. For that I recommend creating a Job that is executed in the background automatically (its last step is to reschedule itself some times later), that checks whether the model changed and refreshes the view.
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.
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) {
...
}
});
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);
}
}