JavaFX TableView Selection Binding doesn't always fire - is there a better work around? - binding

I have the need to disable a button in my application provided nothing is selected or multiple rows are selected.
I created the following binding (extra code to debug it since it wasn't working)..
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(() -> {
boolean result = documentTable.getSelectionModel().getSelectedItems().size() != 1;
return result;
}, documentTable.getSelectionModel().selectedItemProperty());
What happens to me is that when I select the first row, it fires, then if I CTRL + Click the same row, it fires again. So far all is well in the world. Then I Click the same row - nothing, or when I CTRL Click other rows nothing.
My button will stay in the wrong state.
However, if I also add a listener to the property:
ChangeListener<MyDTO> selectedItemsListener = (obs, ov, nv) -> {
boolean result = table.getSelectionModel().getSelectedItems().size() != 1;
System.err.println(result);
};
Then everything works properly.
Is there no other way to handle this?

You need the binding to be invalidated when the list of selected items changes, not when the selectedItem property changes. If one item is selected, that becomes the selected item: if you then select additional items, the selectedItemProperty will not change (it is still the first one selected out of all those selected).
Using your current idiom, just bind to the selected items list:
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(() -> {
boolean result = documentTable.getSelectionModel().getSelectedItems().size() != 1;
return result;
}, documentTable.getSelectionModel().getSelectedItems());
or, more simply,
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(
() -> documentTable.getSelectionModel().getSelectedItems().size() != 1,
documentTable.getSelectionModel().getSelectedItems());
Though it's probably cleaner to use Bindings.size() and IntegerBinding.isNotEqualTo():
BooleanBinding singleDocIsntSelected = Bindings
.size(documentTable.getSelectionModel().getSelectedItems())
.isNotEqualTo(1);

Related

How to add click listener or context menu to Vaadin's Grid column Header

This question is similar to this. But I'm interested in Vaadin 14 as I am migrating old Table components to Grids. There no longer a HeaderClickListener. Therefore, how can you implement click listener on Grid header.
I don't mind adding a context menu with a column selection for copying, but as I understand it, there is no API to get data in a column?
In Vaadin 14 there is no HeaderClickListener. Instead you need to do something like this:
Column<MyBean> column = grid.addColumn(..)
HeaderRow headerRow = this.getHeaderRows().get(0); // Get first header row
Div component = new Div(); // Just example, anything that has click listener can be used
component.setText("Header");
headerRow.getCell(column).setComponent(component);
component.addClickListener(..);
Alternatively you can use GridContextMenu
grid.addColumn(..).setId("column");
GridContextMenu<MyBean> menu = grid.addContextMenu();
item.addMenuItemClickListener(event -> {
Notification.show(selectedColumn);
});
menu.addGridContextMenuOpenedListener(event -> {
// item is not present when clicking header
if (!event.getItem().isPresent()) {
event.getColumnId().ifPresent(id -> {
selectedColumn = id;
});
} else {
menu.close(); // Do not let menu open on body
}
});

Vaadin addComponentColumn only works in last row

this.grid = new Grid<>(Person.class);
this.grid.setItems(personList);
this.grid.setSelectionMode(SelectionMode.MULTI);
this.grid.removeAllColumns();
this.grid.setColumns("firstname");
this.editButton = new Button(null, ImageIcons.EDIT.create());
this.editButton.getStyle().set("color", "#000000");
this.grid.addComponentColumn(person -> this.editButton);
this.deleteButton = new Button(null, IronIcons.DELETE_FOREVER.create());
this.deleteButton.getStyle().set("color", "#000000");
this.grid.addComponentColumn(person -> this.deleteButton);
this.addComponentAsFirst(this.grid);
I have a personList with several entries. The grid shows all these entries with their first name. But it only shows the buttons in the last row. What is the problem?
You use the very same Button instance for each row. You should create a new Button within the componentRenderer, so each row will have its own Button.
Try it like this:
this.grid = new Grid<>(Person.class, false);
this.grid.setItems(personList);
this.grid.setSelectionMode(SelectionMode.MULTI);
this.grid.setColumns("firstname");
this.grid.addComponentColumn(person -> {
// edit: added click listener for inline-editing of the person. Editor must be configured for this to work. See https://vaadin.com/components/vaadin-grid/java-examples/grid-editor
// You don't have to use inline-editing if you don't want. you can also edit the item in a separate Layout with Input fields and a Binder.
Button editButton = new Button(ImageIcons.EDIT.create(), click -> {
this.grid.getEditor().editItem(person);
});
editButton.getStyle().set("color", "#000000");
return editButton;
});
this.grid.addComponentColumn(person -> {
// edit: added click listener for person removal
Button deleteButton = new Button(null, IronIcons.DELETE_FOREVER.create(), click -> {
this.personDao.remove(person);
// TODO: when using an in-memory dataprovider, fetch all items again from service/dao and set them with grid.setItems(this.personDao.findAll());
// but that is not necessary when using a callback dataprovider, which I belive OP is using
this.grid.getDataProvider().refresh();
});
deleteButton.getStyle().set("color", "#000000");
return deleteButton;
}
this.addComponentAsFirst(this.grid);
Edit: a minor thing but I still wanted to mention it - You do some unnecessary creation of columns, only to remove them all again later. Instead of this you could tell the grid not to create these columns in the first place, by passing false as second parameter of the Grid constructor.
this.grid = new Grid(Person.class, false);
// instead of
this.grid = new Grid(Person.class);
this.grid.removeAllColumns();

Vaadin grid - change component column in one row only

I have a grid with several columns. For three columns i used the column renderer. Each of the columns contains one button.
If i click one of those buttons, i want to replace the three buttons in that specific row with two other buttons. All the other rows should not be affected. Is this possible in a Vaadin grid?
Components in different columns don't know each other, as they are all defined in a separate scope (in the componentRenderer of their own column. You cannot define the Button outside of the componentRenderer as you found out in another question today). So the "obvious" solution won't work, where you add a clickListener on the Button to directly change the other buttons.
If you had one column with 3 Buttons inside then this would be much easier.
There is a way, but I see this more as a hack than as a solution. Because you need some extra implementation in the item class for this to work.
In the ComponentRenderer, you can add an if-statement where you look at some value of the item. In one case, you'll render button 1, in the other case you'll render the other button. And in the click listeners of the button you change that value in the item and refresh the dataprovider, so the componentRenderer is invoked again. It will now see the value on the item has changed, therefore displaying some other Button.
Here is some code to show what I mean:
// grid item class
public class Foo {
private boolean buttonPressed = false;
public Foo(){
}
public isButtonPressed(){
return buttonPressed;
}
public setButtonPressed(boolean buttonPressed){
this.buttonPressed = buttonPressed;
}
}
// adding of button columns
// do this 3 times for a test of your scenario.
grid.addComponentColumn(item -> {
if(!item.isButtonPressed()){
return new Button("Before Button was Pressed", click -> {
item.setButtonPressed(true);
grid.getDataProvider().refresh(item);
});
} else {
return new Button("Button was Pressed", click -> {
item.setButtonPressed(false);
grid.getDataProvider().refresh(item);
})
}
})

Unhighlight first highlighted item from AutoComplete

Here I am using p:autoComplete tag in PrimeFaces 5.1, but I can't remove first highlighted/selected item from the suggestions list. How I can remove this ?
Since your "problem" comes from this particular line,
firstItem.addClass('ui-state-highlight');
What happens here is when the suggestions are ready to show up the script highlights the first item of the list, so in your case you would just "unhighlight" that item.
I have created a small function that would do that on every AutoComplete you have, you can call it in your document.ready or where ever you see suitable:
function removeHighlightedFirstItem() {
var oldAutoCompleteShowSuggestions = PrimeFaces.widget.AutoComplete.prototype.showSuggestions;
PrimeFaces.widget.AutoComplete.prototype.showSuggestions = function(query) {
//calling original ShowSuggestions
oldAutoCompleteShowSuggestions.apply(this, [query]);
//after ShowSuggestions
//remove first item highlight
var firstItem = this.items.eq(0);
firstItem.removeClass('ui-state-highlight');
}
}
The result would look like this:
Note: The feature you are requesting is available for 5.1.5, 5.0.14 and 5.2 by adding the autoHighlight = "false" attribute to the component.

could not be able to work with list control in flex 4.5

I want to be selected that new Item in list when I give new value to dataProvider but it selects the item that was firstly selected, and how to make that selected permanently on App start up.
You if want to select the list item after added to dataProvider. Probably you follow this
list.selectedIndex = list.dataProvider.length -1;
Sometimes if you added at desired position like
list.dataProvider.setItemAt(obj,2);
list.selectedIndex = 2
If you want to selected item permanently on App start up.(Make sure that your array collections bind-able to list)
public function onCreationComplete(event:FlexEvent):void
{
callLater(function():void
{
list.selectedIndex = list.dataProvider.length -1;
list.ensureIndexIsVisible(list.selectedIndex); // Viewport
});
}

Resources