Add listener to all cell in column table editable mode of Vaadin - listener

I want add listener to change "." to "," in one column table of vaadin, in editable mode.
I create the table data with BeanItemContainer.

One option is to use a custom TableFieldFactory to then add the listener to each field that needs it.
https://vaadin.com/book/-/page/components.table.html
table.setTableFieldFactory(new MyTableFieldFactory());
...
public class MyTableFieldFactory extends DefaultFieldFactory {
#Override
public Field createField(Container container, Object itemId,
Object propertyId, Component uiContext) {
String prop = (String) propertyId;
if ("a".equals(prop)) { // propertyId of the column you wish to change
AbstractField f = (AbstractField) super.createField(container, itemId, propertyId, uiContext); // casting to AbstractField to set the field to immediate mode
f.setImmediate(true);
f.addValueChangeListener(new Property.ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
String val = (String) event.getProperty().getValue();
val = val.replace(".", ",");
event.getProperty().setValue(val);
}
});
return f;
}
return super.createField(container, itemId, propertyId, uiContext);
}
}

Related

Vaadin How to handle IndexedContainer.Property.ValueChangeEvent for specific Item or PropertyId?

I want to handle Property.ValueChangeEvent in my IndexedContainer, when the value is changed in specific row. I expected the "event" passed as argument will supply the information what has been changed.
IndexedContainer container = new IndexedContainer();
.
. add container properties, insert items ...
.
container.addValueChangeListener(this)
.
.
public void valueChange(ValueChangeEvent event) {
// How to get itemId and another properties from the same item?
event.getProperty(); // This returns only whole container
}
The getProperty() function doesn't give me the information, from which row and column it comes. Thanks for help.JH
Usually a container is used within a component, and is that component that listens for "valueChange".
The way to get the selected item is:
Item itemSelected = component.getContainerDataSource().getItem(component.getValue());
//get for example "id" property
Integer id = (Integer) itemSelected.getItemProperty("id").getValue();
Otherwise, are you just using a container alone? If so can you be more specific? because it's not a "standard" way.
Regards
It's not very efficient, but it works:
container.addValueChangeListener(new ValueChangeListener() {
private static final long serialVersionUID = 1L;
#Override
public void valueChange(ValueChangeEvent event) {
for (Object itemId : container.getItemIds()) {
for (Object propId : container.getContainerPropertyIds()) {
Property<?> p = container.getContainerProperty(itemId, propId);
Object o = ((EventObject) event).getSource();
if (p.equals(o)) {
System.out.println("itemId: " + itemId);
System.out.println("propertyId: " + propId);
return;
}
}
}
}
});
The event is a PropertyValueChangeEvent and the getSource() method returns IndexedContainerProperty.
The problem is that those are private classes.
The IndexedContainerProperty contains the itemId and the propertyId too.

Change item queue in #Html.EnumDropDownListFor

There's this enum
public enum UserStatus
{
Employee = 0,
Evaluation,
Dismissed,
Registered,
}
And on view
#Html.EnumDropDownListFor(model => model.User.Metadata.Status)
So it show me Employee as default option and all other items with enum queue (E,E,D,R). But i'd like to show items in this queue (Evaluation, Registered, Employee, Dismissed) (Mainly Evaluation must be first).
I cant change the enum, and i cant set as default in GET controller (due to model realization).
Any ideas how solve this problem?
I don't think you can change the list during runtime on how it appears. The easiest way i can think to handle such problem ( where you can't change the sequence in which the enum values appear ) is to add attribute on each enum value that defines the sequence number and then extract all the items in a specific enum to create a list which can be binded to the view. This might be an extra work but would solve your issue.
Here's a sample code :
public class Sequence : Attribute
{
public int SequenceNum { get; set; }
}
public enum UserStatus
{
[Sequence(SequenceNum=3)]
Employee = 0,
[Sequence(SequenceNum = 2)]
Evaluation,
[Sequence(SequenceNum = 4)]
Dismissed,
[Sequence(SequenceNum = 1)]
Registered,
}
In your model class :
public IEnumerable<SelectListItem> ListData { get; set; }
public UserStatus Status { get; set; }
In your controller :
List<KeyValuePair<int,string>> KeyValueList = new List<KeyValuePair<int,string>>();
// Get all the enum values
var values = Enum.GetValues(typeof(UserStatus)).Cast<UserStatus>();
// Iterate through each enum value to create a keyvalue list
foreach (var item in values)
{
var type = typeof(UserStatus);
var memInfo = type.GetMember(item.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(Sequence),false);
KeyValueList.Add(new KeyValuePair<int,string>(((Sequence)attributes[0]).SequenceNum,item.ToString()));
}
// Sort the keyvalue list based on the *SequenceNum* attribute
KeyValueList.Sort((firstPair, nextPair) =>
{
return nextPair.Value.CompareTo(firstPair.Value);
});
// Add SelectListItem collection to a model property - Apparently you can add the generate collection to a ViewData ( if you don't wish to create another property )
model.ListData = KeyValueList.Select(x => new SelectListItem { Value = x.Key.ToString(), Text = x.Value }).ToList();
In your View :
#Html.DropDownListFor(m => m.Status, Model.ListData));
When you post the data, the model property Status would be populated with the selected Enum value.
Hope it helps.

Sorting data held by JTable after insertion

I populate a JTable from a database. The data in JTable is sorted based on the auto-generated primary key in descending order. The table looks like the following.
The data in the table is held by a list which contains a list of objects of a JPA entity - List<Country>.
Since I display data in descending order by countryId (primary key), the list needs to be sorted in descending by countryId after data is inserted and before the fireTableRowsInserted(size, size) method is executed.
After sorting this list in descending order by countryId, the table looks wonky as follows.
Values through the given text fields are submitted after validation, when the given add button is pressed.
The row is added to database and to the list and the list is also sorted as mentioned but the data in the table are not shown as they are in the list.
See the last two rows in the preceding snap shot. The actual row which is created is not displayed. The last row is duplicated instead which is different from the underlying list where the new object is added and the list sorted too.
My AbstractTableModel is as follows.
package admin.model;
import entity.Country;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public final class CountryAbstractTableModel extends AbstractTableModel
{
private List<Country> countries;
private List<String> columnNames;
public CountryAbstractTableModel(List<Country> countries)
{
this.countries = countries;
columnNames=getTableColumnNames();
}
//This is the method which sorts the list after adding a JPA entity object.
public void add(Country country)
{
int size = countries.size();
countries.add(country);
Comparator<Country> comparator = new Comparator<Country>()
{
#Override
public int compare(Country o1, Country o2)
{
return o2.getCountryId().compareTo(o1.getCountryId());
}
};
Collections.sort(countries, comparator);
fireTableRowsInserted(size, size);
}
#Override
public String getColumnName(int column)
{
return columnNames.get(column);
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
switch (columnIndex)
{
case 0:
return String.class;
case 1:
return String.class;
case 2:
return String.class;
case 3:
return String.class;
default:
return String.class;
}
}
private List<String> getTableColumnNames()
{
List<String> names = new ArrayList<>();
names.add("Index");
names.add("Id");
names.add("Country Name");
names.add("Country Code");
return names;
}
#Override
public int getRowCount()
{
return countries.size();
}
#Override
public int getColumnCount()
{
return 4;
}
public void remove(List<Long>list)
{
Iterator<Country> iterator = countries.iterator();
while(iterator.hasNext())
{
Country country = iterator.next();
Iterator<Long> it = list.iterator();
while(it.hasNext())
{
if(country.getCountryId().equals(it.next()))
{
iterator.remove();
int index = countries.indexOf(country);
fireTableRowsDeleted(index, index);
break;
}
}
}
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
Country country = countries.get(rowIndex);
switch (columnIndex)
{
case 0:
return rowIndex+1;
case 1:
return country.getCountryId();
case 2:
return country.getCountryName();
case 3:
return country.getCountryCode();
}
return "";
}
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex)
{
Country country = countries.get(rowIndex);
if(value instanceof String)
{
String stringValue = value.toString();
switch(columnIndex)
{
case 2:
country.setCountryName(stringValue);
break;
case 3:
country.setCountryCode(stringValue);
break;
}
}
fireTableCellUpdated(rowIndex, columnIndex);
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return columnIndex>1?true:false;
}
}
If I remove the given Comparator as in the add() method in the code snippet (i.e sorting is not done) then, the table is updated as it should be with the newly created row at the end of the table (which should be on top of the table. Hence sorting is necessary).
Why does this happen when the underlying list is sorted? (Again it doesn't happen, when the list is not sorted, it is left untouched.)
This is happening because you are telling the model that an element has been added at position 'size' (ie the last position in the list) but because you are sorting the list it is actually in the model at position 0 (in this example).
Probably the simplest way to fix this is to call fireTableDataChanged() and not worry about the index - I think your table would have to be pretty big for this to cause performance problems. Otherwise you could use list.indexOf() to find out where your new element ended up after sorting and call fireTableRowsInserted() with the correct indices.

JComboBox Item Change

My JComboBox model contains item like item1, item2, item1. My problem is when I select third item (item1) in JComboBox and check getSelectedIndex() it always returns 0.
If the item is same in my model how can I get index of each item differently? Like:
item1 returns 0
item 2 returns 1
item1 returns 2
It returns index = 0. Because the method getSelectedIndex() use .equals on objects that are in the JComboBox and compare it with the selected one. In your case because item1 is also at index 0 it finds the condition true and returns 0. If you want to get different index then you have to override the getSelectedIndex() method.
An outline of default getSelectedIndex() method of JComboBox found at Java2s:
public int getSelectedIndex() {
Object sObject = dataModel.getSelectedItem();
int i, c;
Object obj;
for (i = 0, c = dataModel.getSize(); i < c; i++) {
obj = dataModel.getElementAt(i);
if (obj != null && obj.equals(sObject))
return i;
}
return -1;
}
You should have something [may be itemName if item object has a name or anything else] different in 2 entries to get desired result. Override getSelectedIndex() and compare the thing that is meant to be differ in all. If both entries are completely same then whats the point of adding it twice?
If two entries in the JComboBox correspond to the same Object, then even if you click item 3 the actual item that is selected will be the first entry of that object (i.e. the one with the lowest index)
I don't think that this will work for the same objects.
A JList has no problems with identical items.
import javax.swing.event.*;
import javax.swing.*;
class TestList {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final String[] items = {"item1", "item2", "item1"};
final JList list = new JList(items);
final JTextField output = new JTextField(15);
JPanel gui = new JPanel();
gui.add(list);
gui.add(output);
list.addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent lse) {
int index = list.getSelectedIndex();
String outputText =
"Index: " +
index +
" Value: " +
items[index];
output.setText(outputText);
}
});
JOptionPane.showMessageDialog(null, gui);
}
});
}
}

Add text background color to JTable column

The motivation is that I want to see trailing spaces in table cells. For instance, if the cell contains "Foo Bar ", I would like to see the space character after "Bar". Is there a way to change the text background color so that I can see all the characters easily in a JTable cell? I'm looking to do this for a whole column.
try modifying the component in the cell defaulttablecellrenderer for column i.
I think arg4 and arg5 are row and column of the table so you can control the renderer for the cells here too.
JTable table = new JTable();
table.getColumn(i).setCellRenderer(new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable arg0, Object arg1,
boolean arg2, boolean arg3, int arg4, int arg5) {
Component component = super.getTableCellRendererComponent(arg0, arg1, arg2, arg3, arg4, arg5);
// modify this component here
// component.setForeground(Color.black.black);
// component.setBackground(Color.black.black);
return component;
}
});
final JTable table = new JTable(tableModel);
table.setPreferredScrollableViewportSize(TABLE_SIZE);
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent mouseEvent) {
int selectedHeader = table.convertColumnIndexToModel(table
.columnAtPoint(mouseEvent.getPoint()));
table.getColumn(table.getColumnName(selectedHeader))
.setCellRenderer(new DefaultTableCellRenderer() {
public void setBackground(Color c) {
super.setBackground(Color.blue);
}
});
};
});