How to add a click Listener to a table's cell in vaadin? - vaadin

I created a multiselectable table in vaadin, but I can't select only a cell in this table when I click it selects all row.
Is there any way of selecting only a cell of a row ?

The title of the question doesn't reflect the actual question inside
Is there any way of selecting only a cell of a row ?
No. The whole point of a Vaadin table is to reflect rows of data in a tabular form. Setting a table to selectable keeps track of the itemIds of the selected rows with the table
You might be able to simulate selecting cells by using a ColumnGenerator in the table, and adding a listener to the generated component. Removing the listener may be tricky, however.
Alternatively, you may wish to simply generate components in a GridLayout and keep track of the selected cells yourself.
Ultimately, the approach here really depends upon exacly what you are trying to achieve.

It depends on what you're trying to accomplish. (Your question title and your question details are addressing two different questions.) If you're wondering whether or not you can target a specific cell and add a click listener to it, then yes, of course you can:
//initial layout setup
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
//Create a table and add a style to allow setting the row height in theme.
final Table table = new Table();
table.addStyleName("components-inside");
//Define the names and data types of columns.
//The "default value" parameter is meaningless here.
table.addContainerProperty("Sum", Label.class, null);
table.addContainerProperty("Is Transferred", CheckBox.class, null);
table.addContainerProperty("Comments", TextField.class, null);
table.addContainerProperty("Details", Button.class, null);
//Add a few items in the table.
for (int i=0; i<100; i++) {
// Create the fields for the current table row
Label sumField = new Label(String.format(
"Sum is <b>$%04.2f</b><br/><i>(VAT incl.)</i>",
new Object[] {new Double(Math.random()*1000)}),
Label.CONTENT_XHTML);
CheckBox transferredField = new CheckBox("is transferred");
//Multiline text field. This required modifying the
//height of the table row.
TextField commentsField = new TextField();
//commentsField.setRows(3);
//The Table item identifier for the row.
Integer itemId = new Integer(i);
//Create a button and handle its click. A Button does not
//know the item it is contained in, so we have to store the
//item ID as user-defined data.
Button detailsField = new Button("show details");
detailsField.setData(itemId);
detailsField.addListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
// Get the item identifier from the user-defined data.
Integer iid = (Integer)event.getButton().getData();
Notification.show("Link " +
iid.intValue() + " clicked.");
}
});
detailsField.addStyleName("link");
//Create the table row.
table.addItem(new Object[] {sumField, transferredField,
commentsField, detailsField},
itemId);
}
//Show just three rows because they are so high.
table.setPageLength(3);
layout.addComponent(table);
It might be beneficial to examine the documentation.

Related

How to dynamically calculate value of a column in edit mode in Vaadin 8 grid component

I have a table in my UI which is a grid component. The value of one of the columns is calculated based on other column input values. So for example there are columns A,B, and C and value of column C = A + B. What I want to achieve is when user edits a row, before clicking the save button it would be able to see the calculated value in edit mode.
Is there a possibility to do that without Javascript or if not, how can I run a javascript snippet on onBlur event of the editable columns (A and B).
The calculated column is not editable by user.
This is a non functional code snippet that shows more or less what is the basis of my code.
Grid<Numbers> grid = new Grid<>(Numbers.class, false);
Editor<Numbers> editor = grid.getEditor();
Grid.Column<Numbers> aColumn = grid
.addColumn(Numbers::getA).setHeader("A")
.setWidth("120px").setFlexGrow(0);
Grid.Column<Numbers> bColumn = grid.addColumn(Numbers::getB)
.setHeader("B").setWidth("120px").setFlexGrow(0);
Grid.Column<Numbers> cColumn = grid.addColumn(Numbers::getC)
.setHeader("C");
Grid.Column<Numbers> editColumn = grid.addComponentColumn(numbers -> {
Button editButton = new Button("Edit");
editButton.addClickListener(e -> {
if (editor.isOpen())
editor.cancel();
grid.getEditor().editItem(numbers);
});
return editButton;
}).setWidth("150px").setFlexGrow(0);
Binder<Numbers> binder = new Binder<>(Numbers.class);
editor.setBinder(binder);
editor.setBuffered(true);
TextField aField = new TextField();
aField.setWidthFull();
binder.forField(aField)
.bind(Numbers::getA, Numbers::setA);
aColumn.setEditorComponent(aField);
TextField bField = new TextField();
bField.setWidthFull();
binder.forField(bField)
.bind(Numbers::getB, Numbers::setB);
bColumn.setEditorComponent(bField);
TextField cField = new TextField();
cField.setWidthFull();
binder.forField(cField)
.bind(Numbers::getC, Numbers::setC);
cColumn.setEditable(false);
Button saveButton = new Button("Save", e -> editor.save());
Button cancelButton = new Button(VaadinIcon.CLOSE.create(),
e -> editor.cancel());
cancelButton.addThemeVariants(ButtonVariant.LUMO_ICON,
ButtonVariant.LUMO_ERROR);
HorizontalLayout actions = new HorizontalLayout(saveButton,
cancelButton);
actions.setPadding(false);
editColumn.setEditorComponent(actions);
I tried adding this to some events:
Page.getCurrent().getJavaScript().execute("alert('the calculated value')");
The problem is the elements that are created by grid component have no id, so accessing them directly is not an option. Traversing dom is not really feasible too.

Bing Maps call infobox from outside table

I have a map on the left and a table on the right. I would like hoverover the table cell and it to pop up the infobox on the map. I have the map mouseover working but don't know how to call it from outside the Map.
I am using MVC.
The table has 50 or so entries and the map has the corresponding pins I want to show the infobox for that pin when the table cell or text his mouse over.
Assume that you already have or can manually create some unique id for each pushpin/table cell pair. Then after you create each pushpin, you can assign this info to its metadata:
var pushpin = new Microsoft.Maps.Pushpin(map.getCenter(), null);
pushpin.metadata = { id: "Some Id" };
// ......
// Other work like binding infobox and mouseover event
Now Let's say you eventually add the pushpins to map like this:
var pushpins = [....];
var layer = new Microsoft.Maps.Layer();
layer.add(pushpins);
map.layers.insert(layer);
And on the table side, bind the same id to a custom data attribute of the table cell:
<td data-pushpinId="Some Id">Cell A</td>
When you hover on the table cell, get its data-pushpinId and find the corresponding pushpin on the layer, then invoke its mouse over event.
var pushpinId = tableCell.getAttribute("data-pushpinId");
var pushpin = layer.getPrimitives().find((pushpin) => pushpin.metadata.id === pushpinId);
pushpin && pushpin.mouseover.invoke();

Table merge cells - Vaadin

I'm Creating table with Vaadin. Some of the cells are repeating. So I want them to merge in one cell, as you can see on the image:
The first image show how the table looks now, and the second is how I want to look with merged cells.
I'm using this code:
Table table = new Table(AppData.getMessage("menu.report2"));
table.addContainerProperty(tableHeaders[0], String.class, null);
table.addContainerProperty(tableHeaders[1], String.class, null);
table.addContainerProperty(tableHeaders[2], String.class, null);
table.addContainerProperty(tableHeaders[3], String.class, null);
List<User> employeeList = employeeDAO.findAllEmployees();
int i;
for (i = 0; i < employeeList.size(); i++) {
User employee = employeeList.get(i);
table.addItem(new Object[]{
CaseStatus.OPEN,
tasksDAO.countTasks(CaseStatus.OPEN),
employee.getFirstAndLastName(),
tasksDAO.countTasks(employee, CaseStatus.OPEN)},
i);
}
for (int j = 0; j < employeeList.size(); j++) {
User employee = employeeList.get(j);
table.addItem(new Object[]{
CaseStatus.CLOSED,
tasksDAO.countTasks(CaseStatus.CLOSED),
employee.getFirstAndLastName(),
tasksDAO.countTasks(employee, CaseStatus.CLOSED)},
i + j);
}
table.setPageLength(table.size());
addComponent(table);
setComponentAlignment(table, Alignment.TOP_CENTER);
setMargin(true);
I think, this is not possible in "normal way". But I have an idea how to simulate this.
First solution:
You could simply use GridLayout to build grid as you want. Then nothing limit your imagination beside size of such grid. It shouldn't be to big (also pointed here).
Second solution:
Another advice is a bit crazy.
Disable Table/Grid stripes.
In group of similar cells just fill only first row. Left rest blank (or space in string).
Then you dispose of repeated data (from your first and second column), but you can't alignment them vertically (the text will stay at top - first row from group).
Third solution:
Check Add-ons list. For example ItemGrid.

VAADIN - set full height of tab content

I have a TabSheet and a tab item.
There is a table inside the tab.
I have set all height to setFullSize but the height of the table does not occupy the whole tab.
Code is here:
public class GvApplication extends Application {
private static Logger log = Logger.getLogger(GvApplication.class.getName());
Window mainWindow;
TabSheet tabsheet;
#Override
public void init() {
setTheme("gv");
mainWindow = new Window("Test");
mainWindow.getContent().setHeight("100%");
tabsheet = new TabSheet();
tabsheet.setSizeFull();
mainWindow.addComponent(tabsheet);
initSMSTab();
setMainWindow(mainWindow);
}
private void initSMSTab() {
VerticalLayout tab = new VerticalLayout();
tab.setMargin(true);
Table table = new Table("Naam");
table.setWidth("100%");
table.setHeight("100%");
table.setSizeFull();
tab.addComponent(table);
tabsheet.addTab(tab);
Tab smsTab = tabsheet.getTab(tab);
smsTab.setCaption("SMS");
}
}
There is a lot of space left under the table. How can I make table use the whole content of the tab?
If a component should occupy all the available space in a layout, you have to invoke setExpandRatio on the layout in addition to invoking setSizeFull on the component. In your case:
tabsheet.setSizeFull();
VerticalLayout rootLayout = new VerticalLayout();
rootLayout.setSizeFull();
rootLayout.addComponent(tabsheet);
rootLayout.setExpandRatio(tabsheet, 1f);
mainWindow.setContent(rootLayout);
By default, when you create new table component, table "page length", it mean rows count, value is 15, this is a reason why table do not set full size of your tab. Only way to set full size is increase table "page length" by table.setPageLength(30).
PS. You can remove this lines, because you already use "table.setSizeFull();"
table.setWidth("100%");
table.setHeight("100%");
EDIT 1
Case with you show on your screens in comment will be only when you resize parent or table component. Try fist of all add ResizeListener to your window and inside listener write something like
// This will return current rendered rows count
int shownRowsCount = table.getVisibleItemIds().size();
table.setPageLength(shownRowsCount);

How to avoid JQuery UI accordion with a long table inside from scrolling to the beginning when new rows are appended?

I have a table of many rows in a JQuery UI accordion.
I dynamically append the table this way:
var resJson = JSON.parse(connector.process(JSON.stringify(reqJson)));
for ( var i in resJson.entryArrayM) {
// test if entry has already been displayed
if ($("#resultTr_" + resJson.entryArrayM[i].id) == null)
continue;
$("#resultTable > tbody:last").append(listEntry.buildEntryRow(resJson.entryArrayM[i]));
}
Firstly I check if a row of the same tr id already exists. If not, I would append to the last row of the table.
It works. But the problem is: every time a row is appended, the accordion would scroll to the first row of the table. Since the table is remarkably long, it makes users inconvenient to scroll down again and again to watch newly-added rows. So how to avoid this?
First of all, just do one append rather than appending every time through the loop:
var resJson = JSON.parse(connector.process(JSON.stringify(reqJson)));
var seen = { };
var rows = [ ];
var trId = null;
for(var i in resJson.entryArrayM) {
// test if entry has already been displayed
var trId = 'resultTr_' + resJson.entryArrayM[i].id;
if($('#' + trId).length != 0
|| seen[trId])
continue;
rows.push(listEntry.buildEntryRow(resJson.entryArrayM[i]));
seen[trId] = true;
}
$("#resultTable > tbody:last").append(rows.join(''));
Also note that I corrected your existence test, $(x) returns an empty object when x doesn't match anything, not null. Not only is this a lot more efficient but you'll only have one scroll position change to deal with.
Solving your scrolling issue is fairly simple: find out what element is scrolling, store its scrollTop before your append, and reset its scrollTop after the append:
var $el = $('#whatever-is-scrolling');
var scrollTop = $el[0].scrollTop;
$("#resultTable > tbody:last").append(rows.join('')); // As above.
$el[0].scrollTop = scrollTop;
There might be a slight visible flicker but hopefully that will be lost in the noise of altering the table.
You could also try setting the table-layout CSS property of the <table> to fixed. That will keep the table from trying to resize its width or the width of its columns and that might stop the scrolling behavior that you're seeing. The downside is that you'll have to handle the column sizing yourself. But, you could try setting table-layout:fixed immediately before your append operation to minimize the hassle.

Resources