What is the best practice for coding addChangeHandler in Gwt? - gwt

in UiBinder
#UiField ListBox testListBox;
in presenter, I have a method getData() that put data into the testListBox.
public void getData(){
getView().getTestListBox().clear();
getView().getTestListBox().addItem("itm 1");
getView().getTestListBox().addItem("itm 2");
getView().getTestListBox().addItem("itm 3");
getView().getTestListBox().addChangeHandler(new ChangeHandler(){
#Override
public void onChange(ChangeEvent event) {
// TODO Auto-generated method stub
int ind=getView().getTestListBox().getSelectedIndex();
System.out.println(getView().getTestListBox().getValue(ind));
}
});
}
Now, i also have a button to call getData(). If i click that button 1 time, then I select "itm 1" in the testListBox everything is fine as it will print out:
itm 1
However, if I click that button 2nd times, & select "itm 1" then it prints out twice "itm 1":
itm 1
itm 1
If i click it 3rd time, it will print out triple "itm 1"....
But if i put the getView().getTestListBox().addChangeHandler outside getData() method before i add the item into the listbox (ie I do the addChangeHandler before there is actual item inside the listbox), then everything is fine as it print out only 1 time.
So What is the best practice for coding addChangeHandler in Gwt?

The important thing is that you call addChangeHandler() only once. If you call it multiple times (as in your scenario) you end up having multiple handlers all getting invoked at the same time when the value changes (hence the repeted values getting printed).
It doesn't really matter if you call addChangeHandler() before or after adding the actual items. I usually add the change handler right after creating the ListBox instance, and that's what I have seen done most often.
Since you are using UIBinder, the ListBox instance gets created automatically for you. In this case a good place to call addChangeHandler() would be in the UI container's constructor. When using MVP, it should probably go in the presenter's bind method.

Related

GWT Activities: Widget added to view again when using browser back button

The way I understand it, if I want to add widgets dynamically to a view created with UIBinder, I would do that in the start method of the activity that is the presenter for that view.
Here's my code:
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
view = clientFactory.getDashboardView();
view.setPresenter(this);
ArrayList<Department> deps = ModelFactory.getDepartments();
view.passData(deps); // Correct?
panel.setWidget(view.asWidget());
}
public void passData(ArrayList<Department> departments) {
TextCell text = new TextCell();
CellList<String> cellList = new CellList<String>(text);
String[] departmentNames = new String[departments.size()];
for (int i = 0; i < departments.size(); i++) {
departmentNames[i] = departments.get(i).getName();
}
cellList.setRowData(Arrays.asList(departmentNames));
departmentsDiv.add(cellList);
}
It works. However, when using the back button to navigate to the previous place and back, the widget is added again.
How do I handle this correctly?
You have two options:
If you don't want to refresh the data on each visit to this view, you need to add a flag to the view to tell if the data has been already populated. Then, when this view is visited again, your activity should call view.passData(deps); only if the flag is set to false. After the data is loaded, set the flag to true.
If you do want to refresh the data on each visit, call departmentsDiv.clear() before adding a new CellList.
NB: A better approach is to create your CellList once, when the view is displayed for the first time, and then only call setRowData when the new data is available.
You are creating View object by using Factory method. You should consider creating views during application load using, for instance: GIN and marking them as Singletons. The proper way is to pass them as start() method parameters and just set presenter reference on them.
General idea is to make Views singletons. Activities should be created while throwing GWT place (stateless) and just using singleton Views, so you can keep your view input data.
Read tutorial here on using MVP / GIN pattern: http://blog.hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-example.html

GWT Detect DOM changes or modifications

What am I trying to do?
I have an existing page (generated by system automatically and I don't have any control on it) in which I am injecting GWT code to modify the behaviour of the page after it loads based on certain columns and augment the functionality of the page. For example after adding my GWT code, cells in one of the table columns become clickable and when the user clicks it, additional information is displayed to the user in a pop-up panel. All that is working fine.
What is the issue?
The generic page in which I am injecting my code has paginated table which shows 15 rows at a time. Now, when I load/refresh the page, my GWT code kicks in and sinks events in the specific column which adds functionality (described above) to the cells. However, when the user uses the left and right buttons to navigate the paginated result, the page does not refresh as it is an asynchronous call. The specific column in the new set of 15 rows is now without the sunk events as the GWT code does not know that the page changed.
I am trying to find a way to tell my GWT code that page has changed and it should sink events to the cells of specific column for the new 15 rows but unable to find any method or mechanism to help me capture a DOM/Document change event. I tried doing this but did not help:
new ChangeHandler(){
#Override
public void onChange(ChangeEvent event) {
Window.alert("Something Changed");
}
It is possible I am missing something very obvious. Posting this question to know if there is an easy way to figure out DOM changes in GWT. Have searched for DOM/Document change/mutation/ etc. without luck.
If anyone knows how to detect DOM changes in GWT would really appreciate otherwise would go ahead writing native code using native mutation observers.
You can try something like this:
First get the input elements with:
InputElement goOn = DOM.getElementById("IdOfGoOnButton").cast();
InputElement goBack = DOM.getElementById("IdOfGoBackButton").cast();
Next add a native EventHandler:
Event.addNativePreviewHandler(new Event.NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(Event.NativePreviewEvent event) {
if (event.getTypeInt() == Event.ONCLICK) {
if (event.getNativeEvent()
.getEventTarget() != null) {
Element as = Element.as(event.getNativeEvent()
.getEventTarget());
if (as.getTagName()
.toLowerCase()
.equals("input")) {
InputElement clickedElement = as.cast();
if (clickedElement.getId().equals(goOn.getId()) ||
clickedElement.getId().equals(goBack.getId())) {
// one of the paging button is pressed
}
}
}
}
}
});
Hope that helps.

GWTP: Correct information flow for popup presenter

i had this task: I have Presenter/View Couple (lets call it ItemListPresenter) showing a Celltable. Now i wanted to edit an item by double-clicking it (or pressing a button, whatever). Then a popup dialog should appear (Lets call it PopupWidget), letting me edit my item properties.
i found a solution how to do it, but i am not sure it is the "right" way. Since i am trying to learn the philosophy behind GWT/GWTP, i would appreciate if you could give me hints what i did right and what i did wrong:
In the onbind method of ItemListPresenter, i wire the CellTable with a DoubleClick handler:
getView().getCellTable().setSelectionModel(selectionModel);
getView().getCellTable().addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(final DoubleClickEvent event) {
DeviceDto selectedDeviceDto = selectionModel.getSelectedObject();
//TODO: Figure out how to best handle editing of DeviceDto
if (selectedDeviceDto != null) {
devicesDialog.setCurrentDeviceDTO(selectedDeviceDto);
addToPopupSlot(devicesDialog);
}
} }, DoubleClickEvent.getType());
What does not feel right is setting the object i want to edit (selectedDeviceDto) in the Dialog Presenter Widget. Is this the "right" way?
My popup presenter which is defined as
public class DeviceEditDialogPresenterWidget extends PresenterWidget<DeviceEditDialogPresenterWidget.MyView> implements
DeviceEditDialogUiHandlers {
is still ugly, since i just set every property into a text box and after editing, i recollect the properties and rebuild the object. This is messy and i guess i should GWT Editors for that. However, when i click the "Save" Button in the dialog, an UiHandler is triggered:
#UiHandler("okButton")
void okButtonClicked(ClickEvent event) {
DeviceDto dev = new DeviceDto(idBox.getText(), deviceIdBox.getText(), typeBox.getText(), firmwareVersionBox.getText(), userBox.getText(), statusBox.getText());
getUiHandlers().updateDevice(dev);
hide();
}
This triggers my DeviceEditDialogPresenterWidget, which itself fires and event:
#Override
public void updateDevice(DeviceDto device) {
eventBus.fireEvent(new DeviceUpdatedEvent(device));
}
This event is caught by a handler in the "mother" presenter with the CellTable, which is wired in the onBind method again:
addRegisteredHandler(DeviceUpdatedEvent.TYPE, new DeviceUpdatedEvent.DeviceUpdatedHandler() {
#Override
public void onDeviceUpdatedEvent(DeviceUpdatedEvent event) {
updateDevice(event.getDevice());
}
});
I would really like to avoid going down a road of messy mistakes, so any hints would be appreciated.
Thanks
Arthur
PresenterWidgets are usually designed to have a setter which is used to set a Model or DTO that the PresenterWidget works on (the same way you did it).
Alternatively you can avoid a PresenterWidget and use an Editor (extends Composite) that you manually add to a PopupPanel or DialogBox in your ListItemEditor.
This way you avoid the complexity of a PresenterWidget. But you have to handle the clicks (i.e. save button) from the ListItemPresenter. I would always try to start small (use a composite) and if you realize that you might need the functionality also in other places, create a PresenterWidget.
Also you don't need the updateDevice method because you pass a reference to your DTO. You only need to fresh the CellTable.
But apart from that your approach looks fine.

GWT ClickableTextCell

I am a newbie to GWT ... need some help in understanding the below class -
What is the use of GWT ClickableTextCell ??
Is there something specific use of it. It uses something called FieldUpdater, why is that used ?
Think of a ClickableTextCell as hot spot. It looks like a regular bit of screen real estate with text in it, but it responds to clicks. What happens when you click it? The click "updates" the field. An update to a field calls the method update(). What does update() do? Whatever you want it to. You provide it by specifying the FieldUpdater. FieldUpdater is an interface, so you can construct one anonymously. Say you have a CellTable, and you have a Column that displays a String inside a ClickableTextCell. You provide your FieldUpdater to the Column:
Column<DataType, String> myIntegerColumn
= new Column<DataType, String>(new ClickableTextCell());
myIntegerColumn.setFieldUpdater(new FieldUpdater<DataType, String>(){
#Override
public void update(int index, DataType object, String value){
// execute code that reacts to a click on this hot spot
}
});
Now whenever the cell gets clicked, that code in update() fires.
A ClickableTextCell is a specific kind of cell. You can see a demo of all the different kinds of cells in this GWT showcase.
This GWT documentation explains what cell widgets are for, goes over all of the different types, and also has examples of how to use them and the ValueUpdater type.

AutocompleteTextView - it almost works until I tabaway from it?

I have an AutoCompleteTextView control serviced by an 'OnClick' Listener. It extracts a list of items from a database and populates the array adapter attached to the control. When I enter sufficient text to isolate an entry in the adapter list (usually about 2 characters) and I select the identified item, the adapterview's 'OnItemClick' Listener is invoked and I am able to identify the selected item, set the text in the AutoCompleteTextView, and execute its performCompletion() method. When this routine completes, the virtual keyboard remains in place. When I 'Tab' away from the control I receive a NullPointerException!
Any suggestions appreciated ...
PS: this display is generated programmatically.
You can use the snippet below to hide the keyboard.
private static void hideSoftKeyboard (View view) {
InputMethodManager imm = (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}