I am using smartgwt ( not the paid license version ) and I have a listgrid with three entries.
Key, Value, Reset.
The reset-field is a button that should reset any changes to the value and here is where I struggle.
I tried to implement it as simple as
#Override
public void onClick(ClickEvent event)
{
DataSource ds = this.grid.getDataSource();
ds.removeData(record);
ds.fetchData();
this.grid.redraw();
}
grid being my ListGrid and record the row that has been clicked to be reseted.
But this only removes the entry, it is there again if I reload ( even with the right value , because that is what my server does if he gets remove-requests), but I would like that it is there immediately after I click the button and not after clicking around a bit.
I assumed the fetchData and redraw request combined would accomplish this.
edit: Okay some more code, this shows my constructor for the ListGrid and the RevertButton which should remove and add the Record again.
private static final String REVERT_NAME = "revertField";
public MyListGrid(final String name)
{
this.setDataSource(PropertyListDS.getInstance(name);
ListGridField keyField = new ListGridField(ConfigurationDataSourceFields.PROPERTY_NAME, "Property");
ListGridField valueField = new ListGridField(ConfigurationDataSourceFields.PROPERTY_VALUE, "Value");
ListGridField revertField = new ListGridField(REVERT_NAME, "Revert to Default");
valueField.setCanEdit(true);
this.setShowRecordComponents(true);
this.setShowRecordComponentsByCell(true);
this.setAutoFetchData(true);
this.setFields(keyField, valueField, revertField);
}
#Override
protected Canvas createRecordComponent(final ListGridRecord record, Integer colNum)
{
String fieldName = this.getFieldName(colNum);
Canvas canvas = null;
if ( REVERT_NAME.equals(fieldName) )
{
canvas = new RevertButton(this, record);
}
return canvas;
}
private class RevertButton extends IButton implements ClickHandler
{
private final MyListGrid grid;
private final ListGridRecord record;
public RevertButton(final MyListGrid grid, final ListGridRecord record)
{
super();
this.setTitle("Revert to Default");
this.grid = grid;
this.record = record;
this.addClickHandler(this);
}
#Override
public void onClick(ClickEvent event)
{
DataSource ds = this.grid.getDataSource();
ds.removeData(record);
ds.fetchData();
this.grid.redraw();
}
}
Do in this way using DSCallback.
DataSource#removeData() is a async call to the server. Either redraw the grid again or fetch the data again after getting response from server that record has been deleted in DSCallback.
DataSource dataSource = grid.getDataSource();
dataSource.removeData(record,new DSCallback() {
#Override
public void execute(DSResponse dsResponse, Object data, DSRequest dsRequest){
Record[] records=dsResponse.getData();//deleted records
grid.fetchData();//fetch data again
}
});
Please have a look at this thread Removing local record from listGrid without committing
Try with ListGrid#saveAllEdits() before fetching the data again.
You can try with ListGrid#removeSelectedData() to remove the currently selected records from this component. If this is a databound grid, the records will be removed directly from the DataSource.
Related
I have a UI bug in a legacy code in our Java project. We display a table, with three columns (HumanReadable, name and value) in a window. In that window, users can click on each cell and update the values. Before that, user clicks the "add" button to add a new row (three new cells). Each cell has a default value, until the user decides to update the value. Now, when the users decides to update the value of the cell, he clicks on the cell and types in the value. The bug is that, once done editing, it keeps the default value in the UI. In the backend, the value has changed (if you click the cell again, it will go into editing mode and show you the value).
I uploaded a short GIF that shows the issue and can be found here.
In that GIF you can see that I updated the default value of the first column to be test. Then I click some other place (to exit the edit mode) and it showed the default value instead of test in the first column.
The method that creates the table:
private void createTable(final Composite parent) {
final Table varTable = new Table(parent, SWT.MULTI);
varTable.setHeaderVisible(true);
varTable.setLinesVisible(true);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(varTable);
varTableViewer = new TableViewer(varTable);
final DataBindingContext bindingContext = new DataBindingContext();
final TableViewerColumn col1 = GuiUtils.createTableColumn(varTableViewer, "Human Readable");
col1.setEditingSupport(new StringEditingSupport(varTableViewer, bindingContext, dataProperty));
col1.getColumn().setWidth(120);
final TableViewerColumn col2 = GuiUtils.createTableColumn(varTableViewer, "Name");
col2.getColumn().setWidth(120);
col2.setEditingSupport(new StringEditingSupport(varTableViewer, bindingContext, nameProperty));
final TableViewerColumn col3 = GuiUtils.createTableColumn(varTableViewer, "Value");
col3.setEditingSupport(new StringEditingSupport(varTableViewer, bindingContext, valueProperty));
KeyBoardNavigationSupport.createSupport(varTableViewer);
input = new WritableList(globalVars, FlowVar.class);
ViewerSupport.bind(varTableViewer, input, BeanProperties.values(new String[] { dataProperty, nameProperty, valueProperty }));
}
The StringEditingSupport class:
public class StringEditingSupport extends ObservableValueEditingSupport {
private class CellEditorPrintValidatorErrors extends TextCellEditor {
public CellEditorPrintValidatorErrors(Composite control) {
super(control);
}
#Override
protected void focusLost(){
if(this.getErrorMessage() != null) {
MessageDialog.openError(this.getControl().getShell(), "Invalid input", this.getErrorMessage());
}
}
}
private final CellEditor cellEditor;
String propertyName;
public StringEditingSupport(final ColumnViewer viewer, final DataBindingContext dbc, final String propertyName) {
super(viewer, dbc);
cellEditor = new TextCellEditor((Composite) viewer.getControl());
this.propertyName = propertyName;
}
public StringEditingSupport(final ColumnViewer viewer, final DataBindingContext dbc, final String propertyName, final ICellEditorValidator validator) {
super(viewer, dbc);
cellEditor = new CellEditorPrintValidatorErrors((Composite) viewer.getControl());
cellEditor.setValidator(validator);
this.propertyName = propertyName;
}
#Override
protected IObservableValue doCreateCellEditorObservable(final CellEditor cellEditor) {
return SWTObservables.observeText(cellEditor.getControl(), SWT.Modify);
}
#Override
protected IObservableValue doCreateElementObservable(final Object element, final ViewerCell cell) {
return BeansObservables.observeValue(element, propertyName);
}
#Override
protected CellEditor getCellEditor(final Object element) {
return cellEditor;
}
public String getErrorMessage(){
return cellEditor.getErrorMessage();
}
}
I believe it has something to do with the StringEditingSupport class. This class allows to edit the value in each cell of table. But I couldn't figure out a way to "update" the value shown in the GUI. As I understand input (of type WritableList) contains all the information. Here is the add button listener method:
private class AddButtonSelectionListener extends SelectionAdapter {
#Override
public void widgetSelected(final SelectionEvent e) {
String name = nameProperty;
String meaning = dataProperty;
final List<String> names = new ArrayList<String>();
final List<String> meanings = new ArrayList<String>();
for (final Object var : input) {
names.add(((FlowVar) var).getName());
meanings.add(((FlowVar) var).getData());
}
int index = 0;
while (names.contains(name)) {
name = nameProperty + ++index;
}
index = 0;
while (meanings.contains(meaning)) {
meaning = dataProperty + ++index;
}
input.add(new FlowVar(name, valueProperty, meaning));
}
}
So, as I understand, I need to somehow bind the input to the UI (the content of each cell). I did try many attempts like trying to set a listener to the whole table (varTableViewer.addSelectionChangedListener) but none of them worked. Is it possible to suggest a way to solve this kind of issue?
If anything is missing, please let me know and I'll add it.
I am trying to use TextField in javafx.
The scenario: I have list view populated with specific objects and edit button to edit the object associated with list cell of list view.
When I click on edit button it redirects me to a pane with editing feature where I can edit the name of that object and save it using a save button.
So I have to put validation on save button to make it enable and disable.
If I edit the name in text field then it should enable the save button otherwise it should remains disabled.
I have tried using different methods on text fields as below.
textField.textPorperty.addListener(listener -> {
//Logic to enable disable save button
});
As I am using list view, this listener gives me old value as previously edited object which does not satisfy my condition.
I can not use
textField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {});
as It does not give me expected behavior.
Can anyone help me to solve this issue?
You need to implement additional logic that decides whether or not a change to the textProperty should change the enablement state of the button. This requires:
a reference to the initial value (on setting the text to the input, f.i. on changes to selection in the list)
a boolean property that keeps the enablement state (below it's called buffering)
a listener to the textField that updates the enablement state as needed
Below is a very simplified example - just to get you started - that extracts those basics into a dedicated class named BufferedTextInput. Buffering is changed internally on:
set to false if the "subject" value is set or a change is committed/discarded
set to true once on being notified on the first change of the textField
More complex logic (like not buffering on detecting a change back to the original value) can be implemented as needed.
/**
* Bind disable property of commit/cancel button to actual change.
* http://stackoverflow.com/q/29935643/203657
*/
public class ManualBufferingDemo extends Application {
private Parent getContent() {
ObservableList<Person> persons = FXCollections.observableList(Person.persons(),
person -> new Observable[] {person.lastNameProperty()});
ListView<Person> listView = new ListView<>(persons);
TextField lastName = new TextField();
Consumer<String> committer = text -> System.out.println("committing: " + text);
BufferedTextInput buffer = new BufferedTextInput(lastName, committer);
Button save = new Button("Save");
save.setOnAction(e -> {
buffer.commit();
});
save.disableProperty().bind(Bindings.not(buffer.bufferingProperty()));
Button cancel = new Button("Cancel");
cancel.setOnAction(e -> {
buffer.flush();
});
listView.getSelectionModel().selectedItemProperty().addListener((source, old, current) -> {
buffer.setSubject(current.lastNameProperty());
});
cancel.disableProperty().bind(Bindings.not(buffer.bufferingProperty()));
VBox content = new VBox(listView, lastName, save, cancel);
return content;
}
public static class BufferedTextInput {
private ReadOnlyBooleanWrapper buffering;
private StringProperty value;
private TextField input;
private Consumer<String> committer;
public BufferedTextInput(TextField input, Consumer<String> committer) {
buffering = new ReadOnlyBooleanWrapper(this, "buffering", false);
value = new SimpleStringProperty(this, "");
this.input = input;
this.committer = committer;
input.textProperty().addListener((source, old, current) -> {
updateState(old, current);
});
input.setOnAction(e -> commit());
}
private void updateState(String old, String current) {
if (isBuffering()) return;
if (value.get().equals(current)) return;
setBuffering(true);
}
public void setSubject(StringProperty value) {
this.value = value;
input.setText(value.get());
setBuffering(false);
}
public void commit() {
committer.accept(input.getText());
this.value.set(input.getText());
setBuffering(false);
}
public void flush() {
input.setText(value.get());
setBuffering(false);
}
public boolean isBuffering() {
return buffering.get();
}
public ReadOnlyBooleanProperty bufferingProperty() {
return buffering.getReadOnlyProperty();
}
private void setBuffering(boolean buffer) {
buffering.set(buffer);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For production use, such direct coupling between view and model (f.i. when needing the buffering for a complete form) isn't good enough, further separation might be needed. See BufferedObjectProperty and its usage in a FX adaption of the infamous AlbumManager example (very crude)
I'm using GXT 3.0 and I want to develop a grid table in it. In table, a cell assigned to be have multiple jobs, like save, delete, update. So I need to develop a grid table which has multiple buttons in a cell. To visualize the problem I'm sharing this image :
I tried to add just a cell via
ColumnConfig.setCell()
method, and It's succeeded. But I must add multiple buttons, or cells to handle events. In short form I need multiple Cells inside a Cell.
I know there is a method called ColumnConfig.setWidget(), but it didn't helped. It just added toolbar(or any widget element) to top(header part).
Remember that I use GXT 3.0
Thanks for any help.
You must use a CompositeCell :
private CompositeCell<ObjectRow> createCompositeCell(){
HasCell<ObjectRow, String> button1 = new HasCell<ObjectRow, String>() {
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 1";
}};
HasCell<ObjectRow, String> button2 = new HasCell<ObjectRow,String>(){
public Cell<String> getCell() {
return new ButtonCell();
}
public FieldUpdater<ObjectRow, String> getFieldUpdater() {
return null;
}
public String getValue(ObjectRow object) {
return "Button 2";
}
};
List<HasCell<ObjectRow, ?>> cells = new ArrayList<HasCell<ObjectRow, ?>>();
cells.add(buton1);
cells.add(button2);
CompositeCell<ObjectRow> compositeCell = new CompositeCell<ObjectRow>(cells);
return compositeCell;
}
You can set a different FieldUpdater for handle button click.
I have a page that has a list of Hibernate entities that is loaded dynamically on display of the page. The list is used to create a DataView, which is used to display a paginated list of the entries in the list in a container. Each entry in the list has a delete icon on it. When the delete icon is pressed, I lazy delete the entry, reload the list with entities (which will no longer contain the lazy deleted entry), and reload the container, but the entry is still there in the container until I reload the whole page. Why?
public class LogPage extends ProjectPage{
#SpringBean
private LogDao logDao;
#SpringBean
private LogEntryDao logEntryDao;
private List<LogEntry> logEntryList;
private DataView<LogEntry> dataView;
private WebMarkupContainer logEntryListContainer;
public LogPage(PageParameters pp) {
super(pp);
Project activeProject = SciProSession.get().getActiveProject();
Log log = null;
if (activeProject.getLog()==null){
log = new Log(activeProject);
log = logDao.save(log);
}else{
log = activeProject.getLog();
}
logEntryList = logEntryDao.findAll();
Collections.sort(logEntryList);
logEntryListContainer = new WebMarkupContainer("logEntryListContainer");
logEntryListContainer.setOutputMarkupId(true);
dataView = (DataView<LogEntry>) new DataView<LogEntry>("logEntryDataView", new ListDataProvider(logEntryList)) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<LogEntry> item) {
final LogEntry logEntry = item.getModelObject();
item.add(new Label("contents", logEntry.getContents()));
item.add(new Label("creator", logEntry.getCreator().toString()));
AjaxActionIcon deleteIcon = new AjaxActionIcon("deleteIcon", ImageIcon.ICON_DELETE){
private static final long serialVersionUID = 1L;
#Override
protected void onClick(AjaxRequestTarget target) {
LogEntry toBeRemoved = logEntryDao.reLoad(logEntry);
toBeRemoved.setDeleted(true);
logEntryDao.save(toBeRemoved);
logEntryList = logEntryDao.findAll();
target.addComponent(logEntryListContainer);
}
};
item.add(deleteIcon);
}
};
dataView.setItemsPerPage(10);
logEntryListContainer.add(dataView);
logEntryListContainer.add(new PagingNavigator("navigator", dataView));
add(logEntryListContainer);
}
}
You are changing what the variable logEntryList points to, but that does not impact what the new ListDataProvider(logEntryList) sees.
Upon reload, what you could do is
logEntryList.clear().addAll(logEntryDao.findAll()) so that the variable to which the data provider points is updated
provide your own DataProvider implementation
You pass in the list of logEntries upon creation of the DataView. Any changes to the list afterwards will not be reflected. Try wrapping the list in a PropertyModel and provide a getter for 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.