I need to dinamically add columns to GXT grid. I can do that, but problem occurs, when I want to input data for rows. Thing is, that not all rows have specific column. So what I want to achieve is to check if given row has specific column and return proper value.
Problem is, that ValueProvider for my column doesn't allow to use arguments in it's methods. So I can't pass column name to ValueProvider, so it could check if given column exists in specific row and return proper data.
Here is my column:
ColumnConfig<SomeClass, String> column = new ColumnConfig<SomeClass, String> (props.attributeValue(name), 150, name);
Here is my ValueProvider
ValueProvider<LimitDTO, String> attributeValue(String name);
And here is my implementation (simplified):
public String getAttributeValue(String name) {
if(this.attributes.get(name) == null) {
return "";
} else {
return this.attributes.get(name);
}
}
But I get build error:
Method public abstract com.sencha.gxt.core.client.ValueProvider<com.example.SomeClass, java.lang.String> attributeValue(java.lang.String s) must not have parameters
SOLUTION
Thanks to your answers I was able to do it. This is my implementation of ValueProvider in case someone will look for solution. It wasn't so hard after all :)
public class CustomValueProvider implements ValueProvider<SomeClass, String> {
public String column;
public CustomValueProvider(String column) {
this.column = column;
}
#Override
public String getValue(SomeClass object) {
if(object.getAttributes().get(column) == null) {
return "";
} else {
return object.getAttributes().get(column);
}
}
#Override
public void setValue(SomeClass object, String value) {
}
#Override
public String getPath() {
return column.getName();
}
}
And here is how I used it
LimitsValueProvider lvp = new LimitsValueProvider(name);
ColumnConfig<SomeClass, String> newColumn = new ColumnConfig<>(lvp, 150, name);
Thanks a lot!
I would suggest, do not use
props.attributeValue(name)
Instead, you can follow the post Dynamic charts in GXT 3 and you can create your own dynamic value providers (See the section value providers), which will take columnId (path) as input and peform the same functionality.
Remember ValueProvider is just an interface and using GWT.create you provides its default implementation.
Related
In my table I have one cell that does not update without interaction with the table.
I found the reason already here Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages
My problem is, the default value of the cells item is LocalDate.MIN and I want my cell to contain "---" as long as the item has this default value. When I update the item, I want the cell to contain the current date string.
Item Class:
public class ItemEv {
private final ObjectProperty<LocalDate> openedAt;
#XmlJavaTypeAdapter(LocalDateAdapter.class)
public final LocalDate getOpenedAt() {
return openedAt.get();
}
public final ObjectProperty<LocalDate> openedAtProperty() {
return this.openedAt;
}
public final void setOpenedAt(LocalDate openedAt) {
this.openedAt.set(openedAt);
}
}
in another CellFactory I set the new value: i.setOpenedAt(LocalDate.now());
this is working but not wanted:
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty().asString());
and this is what I tried so far:
openedAtColumnEv.setCellValueFactory(new Callback<CellDataFeatures<ItemEv, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<ItemEv, String> i) {
if (i.getValue().getOpenedAt().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return i.getValue().openedAtProperty().asString();
}
});
and this:
openedAtColumnEv.setCellValueFactory(cellData -> {
if(cellData.getValue().openedAtProperty().getValue().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return cellData.getValue().openedAtProperty().asString();
});
Both of my tests return either SimpleStringProperty or StringBinding which should be fine.
In my tests I made a mistake where the first return in the IF statement does never return true, then the cell values show the standard string for LocalDate.MIN and get updated immediately when the item property changes.
Im a bit lost on this. Please forgive my bad english, Im not a native speaker.
If the property in the model class is an ObjectProperty<LocalDate>, then the column should be a TableColumn<ItemEv, LocalDate>, not a TableColumn<ItemEv, String>.
Implementing the cellValueFactory directly (typically with a lambda expression) is always preferable to using the legacy PropertyValueFactory class. You never "need to use" a PropertyValueFactory (and never should).
The cellValueFactory is only used to determine what data to display. It is not used to determine how to display the data. For the latter, you should use a cellFactory.
So:
private TableColumn<ItemEv, LocalDate> opendAtColumnEv ;
// ...
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty());
openedAtColumnEv.setCellFactory(column -> new TableCell<ItemEv, LocalDate>() {
#Override
protected void updateItem(LocalDate openedAt, boolean empty) {
super.updateItem(openedAt, empty);
if (openedAt == null || empty) {
setText("");
} else {
if (openedAt.equals(LocalDate.MIN)) {
setText("---");
} else {
// Note you can use a different DateTimeFormatter as needed
setText(openedAt.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
}
}
});
I'm trying to filter Rows in a JTable which contains Columns with numbers.
The filtering is working so far, but it filters over the numbers including the thousands-separators. For example, if there is a row with the number 25689 in one row and I try to filter for this row, i have to use "25.689". So it seems there is a formatting that is performed before the filtering.
I've tried to set an own default renderer and the numbers are shown without the separators but the filtering is the same.
Edit
I've added a full example re-creating my problem:
public class GroupingTest {
JFrame frame= null;
Container pane= null;
JTextField tf=null;
JXTable table=null;
public void searchTable() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
final String searchEx = "(?i)"
+ Pattern.quote(tf.getText());
final RowFilter<TableModel, Object> filter;
filter = RowFilter.regexFilter(searchEx);
table.setRowFilter(filter);
//packAll in edt
Utility.packTableView(table);
} catch (final Exception e) {
return;
}
}
});
}
public void createTable() {
frame = new JFrame();
pane=frame.getContentPane();
tf = new JTextField();
tf.setPreferredSize(new Dimension(200,25));
tf.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void insertUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void changedUpdate(final DocumentEvent e) {
searchTable();
}
});
String[] columnHeaders = {"long","strings"};
DefaultTableModel $model = new DefaultTableModel(columnHeaders, 0) {
#Override
public Class<?> getColumnClass(final int $col) {
if($col == 0) {
return Long.class;
} else if($col == 1){
return String.class;
} else {
return Object.class;
}
}
};
table = new JXTable($model);
table.setDefaultRenderer(Long.class, new DefaultTableCellRenderer() {
#Override
public java.awt.Component getTableCellRendererComponent(final JTable $table,
final Object $value, final boolean $isSelected, final boolean $hasFocus, final int $row,
final int $column) {
super.getTableCellRendererComponent($table, $value, $isSelected, $hasFocus, $row, $column);
if ($value instanceof Long) {
this.setHorizontalAlignment(SwingConstants.RIGHT);
}
return this;
}
});
Object[] line1 = {new Long(23345),"asdf"};
$model.addRow(line1);
Object[] line2 = {new Long(3),"dfw"};
$model.addRow(line2);
pane.add(tf,BorderLayout.NORTH);
pane.add(new JScrollPane(table),BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(300,200));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
GroupingTest gt = new GroupingTest();
gt.createTable();
}
}
The Filtering is working so far, but it filters over the numbers including the thousands-separators.
When the value's format interferes with the expected functioning of sorters and filters then it's time to check if getColumnClass(int columnIndex) in the table model is retrieving the appropriate class (in this case Double).
By default AbstractTableModel implementation of such method returns Object.class which is rendered using the toString() method (that's why you see the thousands-separator) and probably filtered according to the string representation as well. Subclasses of AbstractTableModel (such as DefaultTableModel) inherit this implementation and thus this method should be overriden. For example let's say your table model is DefaultTableModel and the first column is a Double:
DefaultTableModel model = new DefaultTableModel() {
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Double.class
: super.getColumnClass(columnIndex);
}
};
See Sorting and Filtering section of How to Use Tables tutorial for further details.
Update
Given your new MVCE it is clear now what are you trying to achieve. I'd start saying that I've mistakenly assumed your table model holds Double instead of Long which makes no difference about overriding getColumnClass() method (it should be done anyways) but it will make a slight difference in the final solution.
Now, to state the requirements clear, you need to filter that column either:
Users input a number (Long) including grouping character.
Users input a number without grouping character.
The string representation of the value contains the substring typed by the users.
To achieve this goal I'd use a custom RowFilter instead of using a regex filter like you do in your example. This is to have control about the string typed by the user and check the three conditions listed above. I've managed to modify your searchTable() to satisfy the requirements. Note: I've included the queried String as an argument in this method to keep tf text field out of the implementation. Please see the code below:
private void searchTable(final String query) {
RowFilter<TableModel, Integer> filter = null;
if (query.length() > 0) {
filter = new RowFilter<TableModel, Integer>() {
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
for (int i = 0; i < entry.getValueCount(); i++) {
String stringValue = entry.getStringValue(i);
Object entryValue = entry.getValue(i);
String numberString = entryValue instanceof Long
? String.valueOf(entryValue)
: "";
if (stringValue.contains(query) || numberString.contains(query)) {
return true;
}
}
return false;
}
};
}
table.setRowFilter(filter);
}
The flow will be more or less as follows:
If the query length is 0 just let the filter be null. This means the table won't be filtered and all rentries will be included.
If not (1) then prepare a new filter which iterates over the whole row asking if the String representation of the entry or the String value of the entry contains the queried String. While those might look the same thing they are not because Entry#getStringValue(int index) might (and actually does) retrieve a different value than String#valueOf(entry#getValue(int index)). In this case the first one retrieves the Long including grouping separators (or formatted if you prefer) while the second one retrieves the Long with no formatting at all (it means, no grouping separators).
Apply the filter to the table in either case.
I hope the idea is clear enough. If you want to filter a Double then it has to be tweaked a little bit because String.valueOf(double) includes the decimal (not grouping) separator and you might want to remove it before checking if it contains the queried String.
I have a JTable that loads data from a database and then generates a summary row at the bottom of the table. I want the user to be able to sort the table, but I'd like the summary row to always remain at the bottom of the table.
I've been trying to create a custom TableRowSorter, but I'm having some trouble figuring it out and getting it to do what I want.
Anyone know how to do this?
Thanks
You need a class which holds whether the value is a summary or not.
Something like this
public class SummarizedValue {
public Integer value;
public boolean summarized;
public String toString() {
return null == value? "" : value.toString();
}
}
public class SummaryComparator implements Comparator<SummarizedValue> {
public int compare(SummarizedValue v1, SummarizedValue v2) {
if (v1.summarized) return 1;
if (v2.summarized) return -1;
return v1.value.compareTo(v2.value);
}
}
public class SummaryBackComparator implements Comparator<SummarizedValue> {
public int compare(SummarizedValue v1, SummarizedValue v2) {
if (v1.summarized) return 1;
if (v2.summarized) return -1;
return v2.value.compareTo(v1.value);
}
}
You need to display the objects of class SummarizedValue in your column and your row sorter should return SummaryComparator. Also your need to override toggle behaviour in RowSorter to use SummaryBackComparator.
I am validating the status of a record retrieved from the DB by defining an ENUM as below
public enum RecordStatusEnum {
CREATED("CREATED"),
INSERTED("INSERTED"),
FAILED("FAILED");
private String recordStatusValue;
RecordStatusEnum (String status) {
recordStatusValue= status;
}
public boolean isSuccess() {
return (this.equals(CREATED) || this.equals(INSERTED));
}
}
The method isSuccess() is being used to check the status of the retrieved record ( column status from employee)
if (!(employee.getStatus().isSuccess())) {
// return error
}
As per the new requirement, there are a set of conditions introduced say A,B and C; and for them there is a column in the Employee table 'condition'.
So I need to retrieve the status as well as the condition and see if it belongs to a set which has the combination of both.
For eg : isSuccess() should check if in the following:
CREATED and A
CREATED and B
INSERTED and C
This must be achieved such that it is easy for me to add a new combination say 'INSERTED and B' into the list easily.
What is the best approach for the above problem?
Note : in the actual business scenario there are a whole lot more statuses and checks (eg isFailed() canBeModified() etc) with many different combinations
And any method can be suggested even if it doesn't use ENUMS. I mentioned ENUMS, because I dont want to deviate much from the existing implementation
There are many possibilities, but you could do like this (I removed the String status, which doesn't add any value since it's equal to the name of the enum):
public enum RecordStatusEnum {
CREATED(Condition.A, Condition.B),
INSERTED(Condition.C),
FAILED();
private Set<Condition> successConditions;
RecordStatusEnum(Condition... successConditions) {
this.successConditions = EnumSet.copyOf(Arrays.asList(successConditions));
}
public boolean isSuccess(Condition c) {
return successConditions.contains(c);
}
}
EDIT:
Example with two sets of conditions:
public enum RecordStatusEnum {
CREATED(EnumSet.of(Condition.A, Condition.B),
EnumSet.of(Condition.C)),
INSERTED(EnumSet.of(Condition.C),
EnumSet.of(Condition.B),
FAILED(EnumSet.noneOf(Condition.class),
EnumSet.noneOf(Condition.class));
private Set<Condition> successConditions;
private Set<Condition> modificationConditions;
RecordStatusEnum(Set<Condition> successConditions,
Set<Condition> modificationConditions) {
this.successConditions = successConditions;
this.modificationConditions = modificationConditions;
}
public boolean isSuccess(Condition c) {
return successConditions.contains(c);
}
public boolean canBeModified(Condition c) {
return modificationConditions.contains(c);
}
}
You could also compare the ordinal values, like so:
public enum RecordStatusEnum {
CREATED,
INSERTED,
UPDATED,
NEW,
FAILED,
FAILED_NO_DB,
FAILED_CONSTRAINT_VIOLATION;
public boolean isPersisted(RecordStatusEnum status) {
return status.ordinal < NEW.ordinal;
}
public boolean isError(RecordStatusEnum status){
return status.ordinal >= FAILED.ordinal;
}
}
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);