TableView change all the rows when I try to edit one column value in javafx - tableview

I have a TableView that has its items property bound to a class Group's applications property which is declared as :
ObjectProperty<ObservableList<Application>> applications = new SimpleObjectProperty();
it is initialised as :
applications.set(new FXCollections.observableArrayList<Application>());
applications.get().add(new Application());
applications.get().add(new Application());
The class Application class contains some properties and another class Customer declared and initialised as :
ObjectProperty<Customer> customer = SimpleObjectProperty<>(new Customer());
Now there are four columns in the tableView that are editable they are 'ApplicationNo', 'ApplicationDate', 'CustomerId' and 'CustomerName'. The first 2 column's cell value factory is set to Application class's applicationNo and applicationDate properties. and the last 2 column's cell value factory are set to the Application class's customer property's customerId and customerName properties. All the four columns are editable with combobox control that are populated with values from the database.
In addition to the above stated table view, I have a form in the FXML consisting of fields that are there in the Application class and the Customer class. This form is disabled when no table row is selected and is enabled with the values of the Application instance, when any of the table view row is selected.
Now my problem is when I select any row and edit the values in the form or in the table, all the row attain the new value that I enter.
I am not uploading the code because it is very lengthy.
the code for updateItem method of table cell editor
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (control != null) {
updateControl();
}
setGraphic(control);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getItemText());
setGraphic(null);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
Code for cell factory and cell value factory of column applicationNo
applicationNo.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<GroupRow, ApplicationModel>, ObservableValue<ApplicationModel>>() {
//GroupRow is the object that contains the Application object
#Override
public ObservableValue<ApplicationModel> call(CellDataFeatures<GroupRow, ApplicationModel> param) {
return param.getValue().getApplicationProperty();
}
});
applicationNo.setCellFactory(
new Callback<TableColumn<GroupRow, ApplicationModel>, TableCell<GroupRow, ApplicationModel>>() {
#Override
public TableCell<GroupRow, ApplicationModel> call(TableColumn<GroupRow, ApplicationModel> param) {
return new ApplicationTableCellEditor();
//ApplicationTableCellEditor is custom TableCell
}
});
applicationNo.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<GroupRow, ApplicationModel>>() {
#Override
public void handle(CellEditEvent<GroupRow, ApplicationModel> event) {
event.getRowValue().setApplication(event.getNewValue());
tableChanged();
applicationChanged(event.getNewValue());
}
});

Actually I have figured out the actual problem and its solution. For changing Row selection I was using :
tableView.getSelectionModel().selectedItemProperty().addListener(.....);
This actually when I selected the 2nd row after selecting the first row as both the rows contained and equal instance of Application resulting in row1.equals(row2) which in turn did not allow the above listener to fire. So, The unbinding of values never happened and result that the controls remained bound to both the rows.
So as a solution I changed the above to:
tableView.getSelectionModel().selectedIndexProperty().addListener(......);
Here this fired every time I selected a different row thereby unbinding the controls and the properties of the previous row and binding the controls to the properties of the new row. This solved my problem. I would request If any one has any other suggestions please post the answer.

Related

Vaadin Select - fields changed inside binder's apply do not write changes to bean from item

Using Vaadin 14.7.0.
Inside a CRUD editor (Enhanced CRUD Editor) I'm building various fields, amongst which I have a Select.
The Select is initialized with a list of options but I'm also trying to change the items from CRUD form edit to CRUD form edit depending on changes from my underlying database so that the user can select new values.
BindingBuilder<Item, SelectOption> bindingBuilder = binder.forField(s);
if (prop.isMandatory()) {
bindingBuilder.asRequired(requiredI18n);
}
bindingBuilder.bind(new ValueProvider<Item, SelectOption>() {
private static final long serialVersionUID = 1L;
#Override
public SelectOption apply(final Item item) {
ListPropertyDefinition lp = ((ListPropertyDefinition)prop);
Serializable currentValue = item.get(lp.getName());
Collection<SelectOption> sOptions = null;
if (lp.getSelectOptions() != null) {
ListDataProvider<SelectOption> ldp = (ListDataProvider)s.getDataProvider();
sOptions = ldp.getItems();
} else {
sOptions = getNewOptions(item, prop.getName());
s.setItems(sOptions);
}
return new SelectOption("N/A", currentValue);
}
}, new Setter<Item, SelectOption>() {
private static final long serialVersionUID = 1L;
#Override
public void accept(final Item bean, final SelectOption fieldvalue) {
bean.set(prop.getName(), fieldvalue != null ? fieldvalue.getValue() : null);
}
});
Now, if the s.setItems(sOptions) branch is being called then the Select field gets populated with the new values sent by the backend but when I'm saving the item the value that I get is null, regardless of what I select in the select field.
This does not happen when I do not change the items in the select field (i.e. if branch).
I did some debugging for comparing 2 select fields - one that changes its values on the fly and one that has values that don't change... from what I could see the field that has values changing on the fly has a null buffered value as seen in the attached image:
vs the field that does not have its values modified in the binder's apply method:
Not sure if what I'm doing is the right way of "refreshing" a select field's values and / or what should I do so that I get the selected value back in the bean on CRUD form save.
I think you are doing things in overly complicated manner. Based on your code I think your principal challenge is how to set empty selection to be "N/A"
For that you simply need to enable empty selection to be allowed. You need to have one placeholder item for empty selection, for which you generate "N/A" as caption. Then you can just do:
Binder<Item> binder = new Binder<>();
Select<SelectOption> select = new Select<>();
...
select.setEmptySelectionAllowed(true);
select.setEmptySelectionCaption("N/A");
binder.forField(s).bind(Item::getProperty,Item::setProperty);
public class SelectOption {
...
}
// Make the item bean also to follow POJO convention
public class Item {
private SelectOption property;
public SelectOption getProperty() {
return property;
}
public void setProperty(SelectOption property) {
this.property = property;
}
}

Dynamically generated columns in radgrid disappear after postback

Am using radgrid and creating it in aspx but on certain action i add more GridTemplateColumns to the grid.
private void CreateDateColumns(List<DateTime> occurenceList)
{
if (occurenceList != null && occurenceList.Count > 0)
{
int index = 1;
foreach (DateTime occurence in occurenceList)
{
string templateColumnName = occurence.Date.ToShortDateString();
GridTemplateColumn templateColumn = new GridTemplateColumn();
templateColumn.ItemTemplate = new MyTemplate(templateColumnName, index);
grdStudentAttendanceList.MasterTableView.Columns.Add(templateColumn);
templateColumn.HeaderText = templateColumnName;
templateColumn.UniqueName = templateColumnName;
index++;
}
}
}
private class MyTemplate : ITemplate
{
protected RadComboBox rcbAttendance;
private string colname;
private int _index;
public MyTemplate(string cName, int index)
{
colname = cName;
_index = index;
}
public void InstantiateIn(System.Web.UI.Control container)
{
rcbAttendance = new RadComboBox();
rcbAttendance.Items.Add(new RadComboBoxItem("---Select---", "-1"));
rcbAttendance.Items.Add(new RadComboBoxItem("Present", "1"));
rcbAttendance.Items.Add(new RadComboBoxItem("Absent", "2"));
rcbAttendance.Items.Add(new RadComboBoxItem("Leave", "3"));
rcbAttendance.ID = "rcbAttendance" + _index;
container.Controls.Add(rcbAttendance);
}
}
All are fine with creation but when i press save button or any combobox make postback the only dynamically generated columns content disappear and the other columns stay.
What i noticed that columns still in place with headertext but only content are disappeared (in my case content are comboboxes)
After enabling viewstate for grid only header text appear.
What should i do to keep columns contents after postback and get their values ?
When creating template columns programmatically, the grid must be generated completely in the code-behind using the Page_Init event. Then, you must create the templates dynamically in the code-behind and assign them to the ItemTemplate and EditItemTemplate properties of the column. To create a template dynamically, you must define a custom class that implements the ITemplate interface. Then you can assign an instance of this class to the ItemTemplate or EditTemplateTemplate property of the GridTemplateColumn object.
Blockquote
Column templates must be added in the Page_Init event handler, so that the template controls can be added to the ViewState.
Blockquote
Source: Telerik
Basicly, you need to create all GridTemplateColumns in the Page_Init. We had the same problem and this approach fixed it.

oracle adf how to disable selected rows by click on bottun?

I have oracle adf and jdeveloper.
I have a table(multiselection) in jsf page with .
I want to disable selected rows by cliching a button.
Can anyone help me?
I'm assuming you want to disable selection of rows in a table. Your question is not very clear.
Create a bean and an attribute inside it which will handle the table selection. This can have the values "single", "none" etc. Read more about it here:
https://docs.oracle.com/cd/E28280_01/apirefs.1111/e12419/tagdoc/af_table.html
Drag and drop a button, double click it to create the code in the backing bean. Write logic in the button such that it will change the value of the previously defined variable.
Point the rowSelection attribute of the table in your page to the value in the bean.
public class MyBean {
private String rowSelection="multiple"
public String getRowSelection{
return this.rowSelection;
}
public void setRowSelection(String rowSelection){
this.rowSelection=rowSelection;
}
public String DisableRowSelection {
this.rowSelection="none";
return null;
}
public String EnableRowSelection {
this.rowSelection="multiple";
return null;
}
}
The table:
<af:table value="#{bindings.myViewObject.collectionModel}" ...//other attributes
rowSelection="#{myBean.rowSelection}" ...//other attributes
Add a transient attribute to your ViewObject called for example IsSelected then iterate through your view Object and set IsSelected attributte to Y for all selected rows .
and set inline-style for all columns
inlineStyle="#{row.IsSelected eq 'Y' ?'background-color:white;' : '' }"

Editable Combobox

I have a singleton EmployeeDatabase that contains an ObservableList of employees.
This ObsList populates the contents of an editable combobox.
When a user selects one of the employees from the combo box and hits a button, I want that employee (from the database) to be displayed in a toString() in the console.
The problem is, the editable combobox does not let me select an employee directly. I'm supposed to use a StringConverter to turn that String into an Employee.
But the Employee already exists in the singleton database! Do I have to search for them in the database? Or is there an easier way to get the Employee as if the combobox weren't editable, and all I would have to do is cmbx.getSelectionModel().getSelectedItem()?
You don't need to go back to the database, since you already have an ObservableList which contains the Employees that are in there. You should be able to do something like:
final ComboBox<Employee> employeeCombo = new ComboBox<>();
employeeCombo.setItems(EmployeeDatabase.getInstance().getEmployees());
employeeCombo.setConverter(new StringConverter<Employee>() {
#Override
public Employee fromString(String string) {
for (Employee employee : employeeCombo.getItems()) {
if (string.equals(employee.getName())) { // may need more logic...
return employee ;
}
}
Employee employee = new Employee(string);
// if things are set up correctly, this call should both update the database
// and the observable list to which the combo box points
EmployeeDatabase.getInstance().add(employee);
return employee ;
}
#Override
public String toString(Employee employee) {
return employee == null : null ? employee.getName();
}
});
employeeCombo.setEditable(true);

Capture value change event on textfield in table using vaadin

Please support me the problem below.
I have a dialog which contains one table and other components. That table has multi rows and columns. A column contains text fields which is enter data by user.
However, source code is built by some other layers, below:
Dialog component is built by DialogA class
Table component is built by TableB class
TableB call a generator class GeneratorC which generate columns, rows, text fields for this table
If I stay in GeneratorC I can add listener and catch event on text fields in TableB, but I am staying in DialogA I can not catch value change event on that text fields. Although I added listener on TableB but this listener only catch events of column and rows, this listener doesn't catch any events of that text fields.
Actually, i only need catch value change event for text fields. Currently i don't still find any solution for this problem.
The source code segment adds listener to TableB, this source code stay in DialogA:
TableB.addListener(new Property.ValueChangeListener()
{
private static final long serialVersionUID =
1L;
#Override
public void valueChange(
ValueChangeEvent event)
{
String temp = (String)event.getProperty().getValue();
}
});
I am looking forward to your answer.
The listener should be added to the text fields you add in the ColumnGenerator and not to the Table itself:
private class TextFieldColumnGenerator implements Table.ColumnGenerator {
private TextField tf;
#Override
public Component generateCell(final Table source, final Object itemId,
final Object columnId) {
Item item = source.getItem(itemId);
tf.addListener(new FieldEvents.TextChangeListener() {
#Override
public void textChange(TextChangeEvent event) {
doSomething(event.getText());
}
});
return tf;
}
I think this should be right, I wrote it outside an IDE so there could be some errors.

Resources