I created form with editable table and submit button:
#Override
protected void populateItem(final Item<Game> listItem) {
final Game game = listItem.getModelObject();
listItem.setModel(new CompoundPropertyModel<Game>(game));
listItem.add(new TextField("players", Model.of(game.getPlayerResult().getPlayer().getName() + ":"
+ game.getOpponent().getPlayer().getName())));
listItem.add(new TextField("leftSide", Model.of(game.getResult().getLeftSide())));
listItem.add(new TextField("rightSide", Model.of(game.getResult().getRightSide())));
listItem.add(new CheckBox("overtime", Model.of(game.getResult().getOvertime())));
}
now I want to save new value which I insert but when I call:
add(new Button("submit") {
private static final long serialVersionUID = 1L;
#Override
public void onSubmit() {
Iterator<Item<Game>> a = dataView.getItems();
while (a.hasNext()) {
Game game = a.next().getModelObject();
System.out.println(game.getPlayerResult().getPlayer().getName());
}
setResponsePage(new SchedulePage(tournament, table));
}
});
it prints still old value. So how I can get table with new values ?
The way you're doing it, the text fields cannot write back the values into your domain objects.
Learn about models then use the appropriate one:
listItem.add(new TextField("rightSide", new PropertyModel(game, "result.rightSide")));
There are many other options, e.g. safemodel.
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 using glazedlists for JComboBox in java to make a JComboBox searchable and sortable. But there is situation I can't get to solve it.
I have a JComboBox attached with glazedlists. glazedlists takes String array to fill this combobox and make it searchable like this
String[] Values = {"ABC", "DEF", "GHI", "JKL", "MNO"};
JComboBox cmb = new JComboBox();
AutoCompleteSupport.install(cmb , GlazedLists.eventListOf(Values));
This works good but the problem is that I want to add ID along with value coming from Database and for that I am implementing my own custom ListCellRenderer like this
class MyListRenderer extends JLabel implements ListCellRenderer
{
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Object[] itemData = (Object[])value;
setText((String)itemData[1]);
return this;
}
}
cmb.setRenderer(new MyListRenderer());
And to add value in JComboBox, I have to
while(rs.next()){
int id=rs.getInt("company_id");
String category=rs.getString("company_name");
Object[] itemData = new Object[] {id, category};
cmb.addItem(itemData);
}
Now how can I implement my JComboBox with glazedlists while setting my own custom renderer?
Didn't have any success with your way, but I found a solution in an earlier project. You can set the model of the JComboBox instead by doing something like this:
//load the list of objects to use
ContainerClass[] allOptions = ContainerClass.getAll();
//create an EventList with this list and set is as the combobox model
final EventList<ContainerClass> eventList = GlazedLists.eventList(Arrays.asList(allOptions));
comboBox.setModel(new DefaultComboBoxModel<ContainerClass>(allOptions));
//finally initialize the combobox by SwingUtilities if done on a non-UI thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AutoCompleteSupport<ContainerClass> install =
AutoCompleteSupport.install(comboBox, eventList);
install.setFilterMode(TextMatcherEditor.CONTAINS);
install.setHidesPopupOnFocusLost(true);
install.setSelectsTextOnFocusGain(false);
install.setCorrectsCase(false);
}
});
And with a ContainerClass like:
class ContainerClass{
int id;
String company;
//helper method to get all the objects needed
static ContainerClass[] getAll(){
ContainerClass test = new ContainerClass();
test.id = 2;
test.company = "abc";
return new ContainerClass[]{test,test,test};
}
#Override
//this String is what actually will be displayed in the combobox
public String toString(){return "(" + id + ") " + company;}
}
And I'm assuming that your JComboBox has the following type:
JComboBox<ContainerClass> comboBox;
(I had to obfuscate the names of all variables, so there might be errors in the code. Let me know and I will correct them)
So to recap. GlazedLists uses the model to get the names, which again asks the ContainerClass for it's toString() method which will return the name to display in the JComboBox.
As a note, when you call comboBox.getSelectedItem() it will return an object of type ContainerClass, or null if it isn't a valid selection.
UPDATE
If you want to be able to control the order as well as the name, you would need to implement your own model for the ComboBox. Found this that seems to explain it well:
class MyComboBoxModel extends AbstractListModel implements ComboBoxModel {
String[] ComputerComps = { "Monitor", "Key Board", "Mouse", "Joy Stick",
"Modem", "CD ROM", "RAM Chip", "Diskette" };
String selection = null;
public Object getElementAt(int index) {
return ComputerComps[index];
}
public int getSize() {
return ComputerComps.length;
}
public void setSelectedItem(Object anItem) {
selection = (String) anItem; // to select and register an
} // item from the pull-down list
// Methods implemented from the interface ComboBoxModel
public Object getSelectedItem() {
return selection; // to add the selection to the combo box
}
}
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.
Here's a code using wiquery selectable:
SelectableAjaxBehavior selectableAjaxBehavior = new SelectableAjaxBehavior() {
private static final long serialVersionUID = 1L;
#Override
public void onSelection(Component[] components, AjaxRequestTarget ajaxRequestTarget) {
System.out.println("SIZE: " + components.length);
}
};
selectableAjaxBehavior.getSelectableBehavior().setSelectedEvent(new JsScopeUiEvent() {
#Override
protected void execute(JsScopeContext scopeContext) {
scopeContext.append("console.log('ID: ' + ui.selected.id);");
}
});
selectableAjaxBehavior.setFilter(".tooth-image");
add(selectableAjaxBehavior);
I'm trying to use this on images in a class: "tooth-image". When I select some images from the method "onSelection()", I get an empty array of selected components. When triggering select action I see elements' IDs in the browser's console, so it works.
So, maybe someone knows what I have do to stop getting an empty array of selected components ?
in my form I have input text and then dataView:
private class RegistrationForm extends Form<Table> {
private static final long serialVersionUID = 1L;
public RegistrationForm(final Table table) {
super("registrationForm", new CompoundPropertyModel<Table>(table));
setOutputMarkupId(true);
final TextField<String> text = new TextField<String>("name");
add(text);
DataView<Player> dataView = new DataView<Player>("rows", playerDataProvider) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<Player> listItem) {
...
on listItem I add ajaxEventBehavior when I double Click on row:
listItem.add(new AjaxEventBehavior("ondblclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(final AjaxRequestTarget target) {
System.out.println(table.getName());
}
});
Problem is when I double click on table it print null not the value which I have in input TextField. Why ?
I try to update model:
text.updateModel();
or get value from text:
System.out.println(table.getName() + "bbbbbbbbbbbbbbbbb" + text.getInput() + "vv"
+ text.getValue() + "ff");
but with no success.
in form I have also submit button and when I press it every think works. I have problems just with double click
AjaxEventBehavior itself does not submit the form, so you're not getting the text field value. You can use one of the following subclasses instead:
AjaxFormComponentUpdatingBehavior will submit only the one FormComponent it is attached to. It needs to be attached to a FormComponent, so you'll have to use the following if you want to attach it to the entire ListItem:
AjaxFormSubmitBehavior will submit the entire form. This is probably what you need here.
All these behaviors are included in Wicket and their Javadoc is right there with the source code. For the Ajax stuff, check subclasses of AbstractAjaxBehavior, in particular the subclasses of AjaxEventBehavior.