Is there any way to add clickHandlers (or any type of handler) to the headers of the columns in a CellTable? I want to add some sorting functionality to my CellTable and I dont see any methods in the Column or Header classes that will allow this. I used this post to figure out how to use the CellTable.
Workaround for click events:
Header<String> columnHeader = new Header<String>(new ClickableTextCell()) {
#Override
public String getValue() {
return columnName;
}
};
columnHeader.setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
Window.alert("Header clicked!");
}
});
table.addColumn(column, columnHeader);
There is no out of the box way of supporting sort as yet on the CellTable. However there is a manual workaround involving a lot of code drudgery. Refer the classes SortableHeader and SortableColumn in the bike shed under expenses sample. You will find the usage in com.google.gwt.sample.expenses.gwt.client.ExpenseDetails. You can use this until something concrete comes out in the next release.
check out directory: http://google-web-toolkit.googlecode.com/svn/trunk/bikeshed
With the final release of GWT 2.1, has there been any support for sortable columns added to the CellTable? Or is it still a roll your own solution after looking at the bikeshed example?
CellTable<Contact> table = new CellTable<Contact>();
// Create name column.
final TextColumn<Contact> nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.name;
}
};
// Create a data provider.
ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(table);
final List<Contact> list = dataProvider.getList();
for (Contact contact : CONTACTS) {
list.add(contact);
}
final ListHandler<Contact> columnSortHandler = new ListHandler<Contact>(
list);
Header<String> columnHeader = new Header<String>(new ClickableTextCell()) {
#Override
public String getValue() {
return "Name";
}
};
columnHeader.setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
if (Window.confirm("Want to do?")){
nameColumn.setSortable(true);
columnSortHandler.setComparator(nameColumn,
new Comparator<Contact>() {
public int compare(Contact o1, Contact o2) {
if (o1 == o2) {
return 0;
}
// Compare the name columns.
if (o1 != null) {
return (o2 != null) ? o1.name.compareTo(o2.name) : 1;
}
return -1;
}
});
} else nameColumn.setSortable(false);
}
});
// Make the name column sortable.
nameColumn.setSortable(false);
// Create address column.
TextColumn<Contact> addressColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.address;
}
};
// Add the columns.
table.addColumn(nameColumn, columnHeader);
table.addColumn(addressColumn, "Address");
// Add the data to the data provider, which automatically pushes it to the
// widget.
// Add a ColumnSortEvent.ListHandler to connect sorting to the
// java.util.List.
//------------------ Code to add --------------------------------//
VerticalPanel vp = new VerticalPanel();
table.addColumnSortHandler(columnSortHandler);
//------------------ Code end --------------------------------//
// We know that the data is sorted alphabetically by default.
table.getColumnSortList().push(nameColumn);
// Add it to the root panel.
vp.add(table);
RootPanel.get().add(vp);
Related
I am trying to add a ComboBox into the TableView but for some reason I cannot make the conversion. Behind the scenes, I want to make the conversation if the value is
0 then it should display 'free' if the value is 1 then it will say 'taken' in the ComboBox, and once the user changes the value in the ComboBox
it will save its integer value.
I am not sure how to add the converter and it gives the following error at event.getNewValue():
cant convert int to string.
Any help where I am doing wrong?
private final IntegerProperty mode;
public int getMode() {
return mode.get();
}
public void setMode(int mode) {
this.mode.set(mode);
}
public IntegerProperty modeProperty() {
return mode;
}
Set<String> modeList = new HashSet<>();
modeList.add("Free");
modeList.add("Taken");
var converter=modeConverter();
TableColumn<Review, String> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter); //How to apply the converter.
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableList(modeList))));
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue()); //Method cannot be applied java.lang.String. But I already make the conversatin via modeConverter
});
...
private StringConverter modeConverter() {
return new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
if (object == 0) {
return "FREE";
} else {
return "Taken";
}
}
#Override
public Integer fromString(String string) {
if (string.equalsIgnoreCase("free")) {
return 0;
} else {
return 1;
}
}
};
}
You need to use the overloaded method:
forTableColumn​(StringConverter converter,
ObservableList items)
Please note that ComboBox backing list should contain 0, 1 instead of Free, Taken. The converter is responsible for displaying 0 as Free and 1 as Taken.
Also, the TableColumn should be of type <Review, Integer> instead of <Review, String>.
In your code, you can do something as follows:
ObservableList<Integer> modeList = FXCollections.observableList(0, 1);
TableColumn<Review, Integer> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter, modeList)));
Once your basic type is fixed, the following should work:
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue());
});
I have a GWT Celltable column that needs to be sorted alphabetically. I followed the official documentation and other questions posted here, but I have not been able to get it to work.
I want the column to sort both ascending and descending. Currently, the carat symbol shows up next to the column header but nothing happens when it is clicked. There are no errors being thrown in the browser console either.
What am I doing wrong? My obfuscated code -
public class MyClass extends Composite {
//other fields
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
#UiField CellTable<MyObject> myTable = new CellTable<MyObject>();
final ListDataProvider<MyObject> myDataProvider = new ListDataProvider<MyObject>();
#UiConstructor
public MyClass(...) {
initWidget(uiBinder.createAndBindUi(this));
// other initialization
buildMyTable();
}
buildMyTable() {
myDataProvider.addDataDisplay(myTable);
Column<MyObject, String> colA = new Column<MyObject, String>(new TextCell()) {
#Override
public String getValue(MyObject object) {
return object.getName();
}
};
Column<MyObject, String> colB = new Column<MyObject, String>(new TextCell()) {
#Override
public String getValue(MyObject object) {
return object.getAddress();
}
};
// created other columns
colA.setSortable(true);
myTable.addColumn(colA, "COL A");
myTable.addColumn(colB, "COL B");
// added other columns to the table
ListHandler<MyObject> columnSortHandler = new ListHandler<>(myDataProvider.getList());
columnSortHandler.setComparator(colA, new Comparator<MyObject>() {
#Override
public int compare(MyObject o1, MyObject o2) {
if (o1 == o2) {
return 0;
}
if (o1 != null) {
return (o2 != null) ? o1.getName.compareTo(o2.getName) : 1;
}
return -1;
}
});
myTable.addColumnSortHandler(columnSortHandler);
myTable.getColumnSortList().push(colA);
ColumnSortEvent.fire(myTable, myTable.getColumnSortList());
}
}
It is all about how do you add data to the CellTable. For example, you can
myDataProvider.setList(rowData);
or
myTable.setRowData(rowData);
but both above methods will not allow the data to be sorted. That is because, you already have ListHandler defined with an empty list. And it will not notice that the list have changed, so no sorting will be performed.
See ListDataProvider documentation:
Modifications (inserts, removes, sets, etc.) to the list returned by getList() will be reflected in the model. However, mutations to the items contained within the list will NOT be reflected in the model. You must call List.set(int, Object) to update the item within the list and push the change to the display, or call refresh() to push all rows to the displays. List.set(int, Object) performs better because it allows the data provider to push only those rows which have changed, and usually allows the display to re-render only a subset of the rows.
So, you should first getList() and change it like this:
myDataProvider.getList().clear();
myDataProvider.getList().addAll(rowData);
I have a problem trying to sort specific columns from a cell table, whcih is populated from the DB, using RPC. Basically I'm trying to sort the family name column alphabetically, and it's just not working. Table gets fully populated, but sorting does not work.
Any ideas why ?
Thanks in advance
// Create the family name column.
final TextColumn<ContactInfo> familyNameColumn = new TextColumn<ContactInfo>() {
#Override
public String getValue(ContactInfo object) {
return object.getFamilyName();
}
};
table.setColumnWidth(familyNameColumn, 20, Unit.PCT);
// Make the family name sortable
familyNameColumn.setSortable(true);
// Add the columns
table.addColumn(familyNameColumn, UserMenuConstants.FAMILY_NAME_COLUMN);
table.addColumn(familyAdministratorColumn, UserMenuConstants.FAMILY_ADMINISTRATOR_COLUMN);
table.addColumn(apartmentNuberColumn, UserMenuConstants.FAMILY_APARTMENT_NUMBER_COLUMN);
table.addColumn(emailColumn, UserMenuConstants.EMAIL_ADDRESS_COLUMN);
table.addColumn(phoneNumberColumn, UserMenuConstants.PHONE_NUMBER_COLUMN);
DBGetContactInfoAsync rpcService = (DBGetContactInfoAsync) GWT.create(DBGetContactInfo.class);
ServiceDefTarget target = (ServiceDefTarget) rpcService;
String moduleRelativeURL = GWT.getModuleBaseURL() + "DBGetContactInfoImpl";
target.setServiceEntryPoint(moduleRelativeURL);
rpcService.getContacts(new AsyncCallback<List<ContactInfo>>() {
#Override
public void onSuccess(List<ContactInfo> result) {
table.setRowCount(result.size());
ListDataProvider<ContactInfo> dataProvider = new ListDataProvider<ContactInfo>();
dataProvider.addDataDisplay(table);
List<ContactInfo> list = dataProvider.getList();
for (ContactInfo contactInfo : result) {
list.add(contactInfo);
}
ListHandler<ContactInfo> listHandler = new ListHandler<ContactInfo>(result);
listHandler.setComparator(familyNameColumn, new Comparator<ContactInfo>() {
#Override
public int compare(ContactInfo o1, ContactInfo o2) {
return o1.getFamilyName().compareTo(o2.getFamilyName());
}
});
table.addColumnSortHandler(listHandler);
}
#Override
public void onFailure(Throwable caught) {
...
}
});
You are making two copies of your data: result and list. The list is connected with dataProvider:
List<ContactInfo> list = dataProvider.getList();
and the listListener is connected with result:
ListHandler<ContactInfo> listHandler = new ListHandler<ContactInfo>(result);
so you are displaying list but sorting the result.
Just replace
new ListHandler<ContactInfo>(result);
with
new ListHandler<ContactInfo>(list);
and it works.
EDIT:
You can make it even easier and pass the result to the ListDataProvider constructor:
new ListDataProvider<ContactInfo>(result);
Then, you don't need to copy values to the list and just do
new ListHandler<ContactInfo>(dataProvider.getList());
Move most of the code in your onSuccess method out of there - there is no reason to call it each time a data is loaded. For example, you can/should set a Comparator only once, etc.
Tell your table which column to use for sorting:
table.getColumnSortList().push(familyNameColumn);
When you finish loading new data, tell your table to sort it:
ColumnSortEvent.fire(table, table.getColumnSortList());
I am using a simply city SuggestBox where I am getting list of cities from the database and putting them in GWT suggestBox oracle.
After that user can select his city from the suggestBox suggestions and user saves his record. For example, he will select "London" from the suggestbox list.
Now when user saves his record, I will not save "London" in the database for that user, instead I want to save "3" (london ID) in database.
For this what I am doing is like this:
public MultiWordSuggestOracle createCitiesOracle(ArrayList<City> cities){
for(int i=0; i<cities.size(); i++){
oracle.add(cities.get(i).getCity()+","+cities.get(i).getCityId());
}
return oracle;
}
Now, I have the city and cityID both displaying in suggestBox and then can save from there 'city' and 'cityId'.
Everything works fine, but it doesn't looks good:
like it dispays as "London,3" and so on in the suggestBox suggestions..
I don't want to show this 3, how and where can I save this Id(3) for future use?
You can also create your own typed Suggestion-Box. You need to implement "Suggestion" and extend "SuggestOracle".
Super simple version may look:
// CityOracle
public class CityOracle extends SuggestOracle {
Collection<CitySuggestion> collection;
public CityOracle(Collection<CitySuggestion> collection) {
this.collection = collection;
}
#Override
public void requestSuggestions(Request request, Callback callback) {
final Response response = new Response();
response.setSuggestions(collection);
callback.onSuggestionsReady(request, response);
}
}
//CitySuggestion
public class CitySuggestion implements Suggestion, Serializable, IsSerializable {
City value;
public CitySuggestion(City value) {
this.value = value;
}
#Override
public String getDisplayString() {
return value.getName();
}
#Override
public String getReplacementString() {
return value.getName();
}
public City getCity() {
return value;
}
}
// Usage in your code:
// list of cities - you may take it from the server
List<City> cities = new ArrayList<City>();
cities.add(new City(1l, "London"));
cities.add(new City(2l, "Berlin"));
cities.add(new City(3l, "Cracow"));
// revert cities into city-suggestions
Collection<CitySuggestion> citySuggestions = new ArrayList<CitySuggestion>();
for (City city : cities) {
citySuggestions.add(new CitySuggestion(city));
}
//initialize city-oracle
CityOracle oracle = new CityOracle(citySuggestions);
// create suggestbox providing city-oracle
SuggestBox citySuggest = new SuggestBox(oracle);
// now when selecting an element from the list, the CitySuggest object will be returned. This object contains not only a string value but also represents selected city
citySuggest.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
#Override
public void onSelection(SelectionEvent<Suggestion> event) {
Suggestion selectedItem = event.getSelectedItem();
//cast returned suggestion
CitySuggestion selectedCitySuggestion = (CitySuggestion) selectedItem;
City city = selectedCitySuggestion.getCity();
Long id = city.getId();
}
});
Keep the reference from city name to id in a Map<String, Integer> and then look the ID up there before you save it.
I use the org.eclipse.core.databinding framework to bind some Text fields in an SWT application. I add an update strategy to validate the data and to set the value on the model only when the user click on the save button:
UpdateValueStrategy toModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_CONVERT);
if (validator != null) {
toModel.setAfterGetValidator(validator);
}
UpdateValueStrategy fromModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE);
binding = bindingContext.bindValue(SWTObservables.observeText(this, SWT.Modify),
BeansObservables.observeValue(pVO, propertyName), toModel, fromModel);
This piece of code works really well.
But how can I do the same on a TableViewer?
I want it to work so that when I add something in the IHM, the model stay unchanged until I call getBindingContext().updateModels();
You do not need use the JFace Databinding Framework in TableViewer. Manipulation the structured data is simpler then SWT controls, such TableViewer, ListViewer and TreeViewer. You can use those viewer in the same way:
create viewer
set content provider
set label provider (suggested)
set filter (optional)
set sorter (optional)
After the viewer created, just invoke viewer.setInput(data) to put all the things to your viewer.
There are a list of model:
TableViewer tableViewer = new TableViewer(parent);
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);`
for (int i = 0; i < COLUMN_NAMES.length; i++) {
TableColumn tableColumn = new TableColumn(table, SWT.LEFT);
tableColumn.setText(COLUMN_NAMES[i]);
tableColumn.setWidth(COLUMN_WIDTHS[i]);
}
tableViewer.setContentProvider(new ModelContentProvider());
tableViewer.setLabelProvider(new ModelLabelProvider());
tableViewer.setInput(models);
The magic happens in the content provider:
class ModelContentProvider implements IStructuredContentProvider {
#SuppressWarnings("unchecked")
#Override
public Object[] getElements(Object inputElement) {
// The inputElement comes from view.setInput()
if (inputElement instanceof List) {
List models = (List) inputElement;
return models.toArray();
}
return new Object[0];
}
/* ... other methods */
}
Each model will become a TableItem and the model in the TableItem(item.getData()).
However, a table composed by many columns, you need the LabelProvider to help you mapping the property of model to the TableItem:
class ModelLabelProvider extends LabelProvider implements
ITableLabelProvider {
#Override
public Image getColumnImage(Object element, int columnIndex) {
// no image to show
return null;
}
#Override
public String getColumnText(Object element, int columnIndex) {
// each element comes from the ContentProvider.getElements(Object)
if (!(element instanceof Model)) {
return "";
}
Model model = (Model) element;
switch (columnIndex) {
case 0:
return model.getFoo();
case 1:
return model.getBar();
default:
break;
}
return "";
}
}
The propagation of models to viewer is easy. If you will propagate viewer to the binded model, using the CellEditor is simple as well.
To use CellEditor, you need set the column properties, cell editors and cell modifier to TableViewer:
tableViewer.setColumnProperties(COLUMNS_PROPERTIES);
tableViewer.setCellEditors(new CellEditor[] {
new TextCellEditor(table), new TextCellEditor(table) });
tableViewer.setCellModifier(new ModelCellModifier(tableViewer));
The CellModifier likes this:
class ModelCellModifier implements ICellModifier {
TableViewer viewer;
public ModelCellModifier(TableViewer viewer) {
this.viewer = viewer;
}
#Override
public boolean canModify(Object element, String property) {
// property is defined by viewer.setColumnProperties()
// allow the FOO column can be modified.
return "foo_prop".equals(property);
}
#Override
public Object getValue(Object element, String property) {
if ("foo_prop".equals(property)) {
return ((Model) element).getFoo();
}
if ("bar_prop".equals(property)) {
return ((Model) element).getBar();
}
return "";
}
#Override
public void modify(Object element, String property, Object value) {
if ("foo_prop".equals(property)) {
TableItem item = (TableItem) element;
((Model) item.getData()).setFoo("" + value);
// refresh the viewer to show the changes to our user.
viewer.refresh();
}
}
}
Everything is simple but there are many steps to make all together.
Use ViewerSupport:
TableViewer tableViewer = ...
IObservableList tableElements = ...
IValueProperty[] columnProperties = ...
ViewerSupport.bind(tableViewer, tableElements, columnProperties);
i agree with qualidafial.
Snippet017TableViewerWithDerivedColumns from the jface.databinding snippets is a full example of this.